2009-05-11 18 views
7

que tienen un gran archivo XML (aproximadamente 10 MB.) En la siguiente estructura sencilla:¿La forma más rápida de agregar un nuevo nodo al final de un xml?

<Errors> 
    <Error>.......</Error> 
    <Error>.......</Error> 
    <Error>.......</Error> 
    <Error>.......</Error> 
    <Error>.......</Error> 
</Errors> 

Mi necesidad es escribir añadir un nuevo nodo <error> al final antes de que el/Errores > etiqueta <. ¿Cuál es la forma más rápida de lograr esto en .net?

+0

¿Cómo estás escribiendo esto? ¿Usando DOM? ¿SAXÓFONO? Escrituras directas? :-P Personalmente prefiero el enfoque DOM; solo tiene acceso al nodo Errores y llama a appendChild(). –

+2

Defina "más rápido" para esta situación; ¿Quiere decir "más rápido de ejecutar" o "más rápido de desarrollar"? –

+0

"más rápido de ejecutar" –

Respuesta

10

Debe utilizar la técnica de inclusión XML.

Su error.xml (no cambia, sólo un trozo Utilizado por los analizadores XML para leer.):

<?xml version="1.0"?> 
<!DOCTYPE logfile [ 
<!ENTITY logrows  
SYSTEM "errorrows.txt"> 
]> 
<Errors> 
&logrows; 
</Errors> 

Su archivo errorrows.txt (cambios, el analizador XML no lo entienden) :

<Error>....</Error> 
<Error>....</Error> 
<Error>....</Error> 

Luego, para añadir una entrada a errorrows.txt:

using (StreamWriter sw = File.AppendText("logerrors.txt")) 
{ 
    XmlTextWriter xtw = new XmlTextWriter(sw); 

    xtw.WriteStartElement("Error"); 
    // ... write error messge here 
    xtw.Close(); 
} 

O incluso se puede utilizar .NET 3.5 XElement, y anexar el texto a la StreamWriter:

using (StreamWriter sw = File.AppendText("logerrors.txt")) 
{ 
    XElement element = new XElement("Error"); 
    // ... write error messge here 
    sw.WriteLine(element.ToString()); 
} 

Ver también Microsoft's article Efficient Techniques for Modifying Large XML Files

0

El método más rápido es probable que sea la lectura en el archivo usando una XmlReader, y simplemente replicar cada uno leer nodo a un nuevo flujo usando XmlWriter Al llegar al punto en el que se encuentra con la etiqueta de cierre </Errors>, a continuación, sólo tiene para generar su elemento adicional <Error> antes de finalizar el ciclo "leer y duplicar". De esta manera, inevitablemente va a ser más difícil que leer todo el documento en DOM (XmlDocument clase), pero para archivos XML grandes, mucho más rápido. Es cierto que usar StreamReader/StreamWriter sería algo más rápido aún, pero es bastante horrible trabajar con código.

0

¿Cómo se representa su archivo XML en el código? ¿Usas las clases System.XML? En este caso, podría usar XMLDocument.AppendChild.

7

En primer lugar, descalificaría System.Xml.XmlDocument porque it is a DOM requiere analizar y construir todo el árbol en la memoria antes de que pueda agregarse. Esto significa que sus 10 MB de texto tendrán más de 10 MB en la memoria. Esto significa que es "intensivo de memoria" y "consume mucho tiempo".

En segundo lugar, descalificaría System.Xml.XmlReader porque es requires parsing the entire file primero antes de que pueda llegar al punto en que puede agregarlo. Tendría que copiar el XmlReader en un XmlWriter ya que no puede modificarlo. Esto requiere la duplicación de su XML en la memoria antes de poder agregarlo.

La solución más rápida a XmlDocument y XmlReader sería la manipulación de cadenas (que tiene sus propios problemas de memoria):

string xml = @"<Errors><error />...<error /></Errors>"; 
int idx = xml.LastIndexOf("</Errors>"); 

xml = xml.Substring(0, idx) + "<error>new error</error></Errors>"; 

cortar la etiqueta final, añadir en el nuevo error, y añadir la etiqueta de final de vuelta.

Supongo que podría volverse loco con esto y truncar su archivo por 9 caracteres y anexarlo. No tendría que leer en el archivo y dejaría que el SO optimice la carga de la página (solo tendría que cargarse en el último bloque o algo así).

System.IO.FileStream fs = System.IO.File.Open("log.xml", System.IO.FileMode.Open, System.IO.FileAccess.ReadWrite); 
fs.Seek(-("</Errors>".Length), System.IO.SeekOrigin.End); 
fs.Write("<error>new error</error></Errors>"); 
fs.Close(); 

que llegará a un problema si el archivo está vacío o contiene sólo "<errores> </Errores >", ambos de los cuales se pueden manejar fácilmente marcando la longitud.

+0

OpenText() abre un archivo para leer y devuelve un StreamReader. –

+0

De hecho, gracias. ¿Fijo? –

+0

greate! Has resuelto un problema muy grande, no sé por qué esta respuesta no ha sido votada 1k –

3

La manera más rápida probablemente sea un acceso directo a archivos.

using (StreamWriter file = File.AppendText("my.log")) 
{ 
    file.BaseStream.Seek(-"</Errors>".Length, SeekOrigin.End); 
    file.Write(" <Error>New error message.</Error></Errors>"); 
} 

Pero pierde todas las características agradables de XML y puede dañar el archivo fácilmente.

+1

Eso es lo que yo habría sugerido también. –

+0

Estoy intentando esto, pero obtengo una 'Incapaz de buscar hacia atrás para sobrescribir datos que existían previamente en un archivo abierto en el modo Añadir'. error en la línea .Seek Es el ejemplo correcto? – Simon

+0

No, el examen no es correcto, pero todo lo que necesita hacer para que funcione es reemplazar 'File.AppendText (...)' con 'new StreamWriter (File.Abrir (filePath, FileMode.Open, FileAccess.Write) ' –

1

Usaría XmlDocument o XDocument para cargar su archivo y luego manipularlo en consecuencia.

Me gustaría ver la posibilidad de almacenar en caché este XmlDocument en la memoria para que pueda acceder al archivo rápidamente.

¿Para qué necesitas la velocidad? ¿Ya tiene un cuello de botella de rendimiento o está esperando uno?

+0

XmlDocument es un modelo DOM que es más lento que SAX como en XmlReader. XmlDocument requeriría representar los 10 MB completos en la memoria como objetos (por lo que más de 10 MB en total). XmlReader sería más rápido (estoy bastante seguro de que XmlDocument está basado en XmlReader), pero aún tiene que analizar todo el documento. Ninguno, para mí, califica como "rápido" si todo lo que Ramesh está haciendo es agregar a un archivo de registro (que parece ser el caso). –

+0

Estoy totalmente de acuerdo, pero siempre evitaría escribir XML con texto anexado. Mi respuesta fue averiguar si podía cargar el documento en la memoria y luego escribir sobre eso. Eso sería rápido. Luego, otro proceso que escribe el XmlDocument en el archivo ocasionalmente. Todo depende del escenario. –

1

Trate de hacer esto:

 var doc = new XmlDocument(); 
     doc.LoadXml("<Errors><error>This is my first error</error></Errors>"); 

     XmlNode root = doc.DocumentElement; 

     //Create a new node. 
     XmlElement elem = doc.CreateElement("error"); 
     elem.InnerText = "This is my error"; 

     //Add the node to the document. 
     if (root != null) root.AppendChild(elem); 

     doc.Save(Console.Out); 
     Console.ReadLine(); 
+1

Esta definitivamente no es la manera más rápida. –

0

es como se hace en C Aquí, .NET debe ser similar.

El juego consiste simplemente en saltar al final del archivo, saltear la etiqueta, agregar la nueva línea de error y escribir una nueva etiqueta.

#include <stdio.h> 
#include <string.h> 
#include <errno.h> 

int main(int argc, char** argv) { 
     FILE *f; 

     // Open the file 
     f = fopen("log.xml", "r+"); 

     // Small buffer to determine length of \n (1 on Unix, 2 on PC) 
     // You could always simply hard code this if you don't plan on 
     // porting to Unix. 
     char nlbuf[10]; 
     sprintf(nlbuf, "\n"); 

     // How long is our end tag? 
     long offset = strlen("</Errors>"); 

     // Add in an \n char. 
     offset += strlen(nlbuf); 

     // Seek to the END OF FILE, and then GO BACK the end tag and newline 
     // so we use a NEGATIVE offset. 
     fseek(f, offset * -1, SEEK_END); 

     // Print out your new error line 
     fprintf(f, "<Error>New error line</Error>\n"); 

     // Print out new ending tag. 
     fprintf(f, "</Errors>\n"); 

     // Close and you're done 
     fclose(f); 
} 
0

Utilizando técnicas basadas en cadenas (como buscar hasta el final del archivo y luego se mueve hacia atrás la longitud de la etiqueta de cierre) es vulnerable a inesperado pero variaciones perfectamente legales en la estructura del documento.

El documento podría terminar con cualquier cantidad de espacio en blanco, para elegir el problema más probable que encuentre. También podría terminar con cualquier cantidad de comentarios o instrucciones de procesamiento. ¿Y qué sucede si el elemento de nivel superior no se llama Error?

Y aquí es una situación que el uso de la manipulación de cadenas falla completamente para detectar: ​​

<Error xmlns="not_your_namespace"> 
    ... 
</Error> 

Si utiliza un XmlReader para procesar el XML, si bien no puede ser tan rápido como tratando de EOF, sino que también permitirá usted para manejar todas estas posibles condiciones de excepción.

+0

El archivo que ha presentado parece un archivo de registro y supongo que está llegando a un punto en el que cada vez es más lento agregarlo, de ahí su pregunta. Baste decir que creo que el formato de registro está completamente bajo su control. –

+0

A menudo puede ser perfectamente correcto hacer esas suposiciones. Sin embargo, tuve que corregir una gran cantidad de código donde el desarrollador suponía equivocado. En la mayoría de esos casos, el desarrollador ni siquiera sabía que estaba adivinando. –

Cuestiones relacionadas