2009-05-11 14 views
7

Mam dużego pliku xml (około 10 MB). W następstwie prostą strukturę:Najszybszym sposobem dodania nowego węzła do końca xml?

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

My potrzebujemy to napisać dodać nowy węzeł < błąd > na końcu przed </błędów > tagu. Jaki jest najszybszy sposób na osiągnięcie tego w .net?

+0

Jak to piszesz? Korzystasz z DOM? SAX? Direct pisze? :-P Osobiście preferuję podejście DOM; wystarczy uzyskać dostęp do węzła błędów i wywołać appendChild(). –

+2

Zdefiniuj "najszybciej" w tej sytuacji; masz na myśli "najszybszy do wykonania" lub "najszybciej rozwijający się"? –

+0

"najszybszy do wykonania" –

Odpowiedz

10

Musisz użyć techniki włączania XML.

Twój error.xml (nie zmienia, tylko zalążek Używany przez parsery XML do odczytu.):

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

plik errorrows.txt (zmiany, parser XML nie rozumie) :

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

Następnie, aby dodać wpis do errorrows.txt:

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

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

Albo można nawet użyć .NET 3.5 Xelement i dołączyć tekst do StreamWriter:

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

Zobacz także Microsoft's article Efficient Techniques for Modifying Large XML Files

0

Najszybszą metodą jest prawdopodobnie czytania w pliku przy użyciu XmlReader, i po prostu replikacji każdy czytać węzeł do nowego strumienia używając XmlWriter Gdy dojdziesz do punktu, w którym można napotkać tag zamykający </Errors>, następnie wystarczy aby wydrukować dodatkowy element <Error> przed kontynuowaniem cyklu "odczyt i duplikat". Ta metoda nieuchronnie będzie trudniejsza niż czytanie całego dokumentu w klasie DOM (klasa XmlDocument), ale w przypadku dużych plików XML, znacznie szybsza o. Prawdą jest, że korzystanie z StreamReader/StreamWriter byłoby jeszcze trochę szybsze, ale dość okropne do pracy z kodem.

0

W jaki sposób plik XML jest reprezentowany w kodzie? Czy używasz klas System.XML? W takim przypadku możesz użyć XMLDocument.AppendChild.

7

Po pierwsze, dyskwalifikuję plik System.Xml.XmlDocument, ponieważ it is a DOM wymaga analizowania i budowania całego drzewa w pamięci, zanim będzie można do niego dołączyć. Oznacza to, że Twoje 10 MB tekstu będzie miało więcej niż 10 MB pamięci. Oznacza to, że jest "czasochłonny" i "czasochłonny".

Po drugie, dyskwalifikuję System.Xml.XmlReader, ponieważ jest to requires parsing the entire file, zanim będzie można przejść do momentu, w którym można do niego dołączyć. Musiałbyś skopiować XmlReader do XmlWriter, ponieważ nie możesz go zmodyfikować. Wymaga to powielenia XML-a w pamięci, zanim będzie można do niego dołączyć.

Im szybciej rozwiązanie XmlDocument i XmlReader byłoby manipulacji ciąg (który ma swoje problemy z pamięcią):

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

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

odciąć znacznika końcowego, dodać nowego błędu i dodać znacznik końcowy powrotem.

Przypuszczam, że można oszaleć i skasować plik o 9 znaków i dołączyć do niego. Nie musiałby czytać w pliku i pozwalałby systemowi optymalizować ładowanie strony (musiałby załadować tylko ostatni blok lub coś takiego).

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(); 

To trafi problem, jeśli plik jest pusty lub zawiera tylko „< Błędy > </Błędy >”, z których oba mogą być łatwo obsługiwane przez sprawdzenie długości.

+0

OpenText() otwiera plik do odczytu i zwraca StreamReader. –

+0

Rzeczywiście, dziękuję. Naprawiony? –

+0

greate! Rozwiązałeś bardzo duży problem, nie wiem, dlaczego ta odpowiedź nie jest przegłosowana ponad 1k –

3

Najszybszym sposobem będzie prawdopodobnie bezpośredni dostęp do plików.

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

Ale tracisz wszystkie ładne funkcje XML i możesz łatwo uszkodzić plik.

+1

To też bym zasugerował. –

+0

Próbuję tego, ale otrzymuję komunikat "Nie można wyszukać wstecz, aby nadpisać dane, które wcześniej istniały w pliku otwartym w trybie Append". błąd w linii .Seek. Czy przykład jest poprawny? – Simon

+0

Nie, egzamin nie jest poprawny, ale wszystko, co musisz zrobić, aby działało, to zamień "File.AppendText (...)" na "nowy StreamWriter (File.Otwórz (filePath, FileMode.Open, FileAccess.Write) ' –

1

Chciałbym użyć XmlDocument lub XDocument, aby załadować plik, a następnie odpowiednio nim manipulować.

Chciałbym wtedy spojrzeć na możliwość buforowania tego XmlDocument w pamięci, dzięki czemu można uzyskać szybki dostęp do pliku.

Do czego potrzebujesz prędkości? Czy masz już jakieś wąskie gardło wydajności, czy też tego oczekujesz?

+0

XmlDocument to model DOM, który jest wolniejszy niż SAX w XmlReader. XmlDocument wymagałby reprezentowania całego 10 MB w pamięci jako obiekty (czyli więcej niż 10 MB łącznie). XmlReader byłby szybszy (jestem prawie pewien, że XmlDocument jest zbudowany na XmlReader), ale nadal musisz przeanalizować cały dokument. Nie kwalifikuję się również jako "szybki", jeśli Ramesh robi to dołączenie do pliku dziennika (co wydaje się być w tym przypadku). –

+0

Całkowicie się zgadzam, ale zawsze unikam pisania XML z dodatkami tekstowymi. Moją odpowiedzią było dowiedzieć się, czy mógłby załadować dokument do pamięci, a następnie napisać do niego. To byłoby szybkie. Następnie inny proces, który od czasu do czasu zapisuje plik XmlDocument do pliku. Wszystko zależy od scenariusza. –

1

Spróbuj tego:

 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

To zdecydowanie nie jest najszybszy sposób. –

0

Oto jak to zrobić w C, powinna być podobna .NET.

Gra polega na prostym przejściu do końca pliku, pomijaniu tagu, dodawaniu nowego wiersza błędu i pisaniu nowego znacznika.

#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

Zastosowanie technik ciągów oparte (jak dążenie do końca pliku, a następnie cofając długość znacznika zamykającego) jest podatny na nieoczekiwany ale całkowicie legalne różnice w strukturze dokumentu.

Dokument może kończyć się dowolną ilością białych znaków, aby wybrać najłatwiejszy problem, jaki napotkasz. Może również kończyć się dowolną liczbą komentarzy lub instrukcji przetwarzania. A co się stanie, jeśli element najwyższego poziomu nie ma nazwy Error?

A oto sytuacja, że ​​za pomocą manipulacji ciąg nie całkowicie do wykrycia:

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

Jeśli używasz XmlReader przetwarzać XML, a to może nie być tak szybko, jak dążenie do EOF, będzie to również pozwolić do obsługi wszystkich tych możliwych warunków wyjątku.

+0

Plik, który przedstawił, wygląda jak plik dziennika i zakładam, że trafia w punkt, w którym coraz wolniej dołącza się do niego, stąd jego pytanie. Wystarczy powiedzieć, że myślę, że format dziennika jest całkowicie pod jego kontrolą. –

+0

Często może być całkiem dobrze, aby te założenia. Musiałem naprawić wiele kodu, ale deweloper nie miał racji. W większości przypadków deweloper nie wiedział nawet, że zgadywał. –

Powiązane problemy