2009-06-17 15 views
6

Ich habe einen Prozess, der eine Reihe von "XML" -Dateien abgreift. Der Grund, warum ich xml in Anführungszeichen gesetzt habe, ist der, dass der Text in der Datei kein Root-Element hat, das ungültiges XML erzeugt. Bei meiner Verarbeitung möchte ich das korrigieren und jede Datei öffnen, am Anfang und am Ende jeder Datei einen Wurzelknoten hinzufügen und dann schließen. Hier ist, was ich im Sinn hatte, aber das beinhaltet das Öffnen der Datei, das Lesen der gesamten Datei, das Markieren der Knoten und das anschließende Schreiben der gesamten Datei. Diese Dateien können mehr als 20 MB groß sein.Hinzufügen von Text zu Anfang und Ende der Datei in C#

 foreach (FileInfo file in files) 
     { 
      //open the file 
      StreamReader sr = new StreamReader(file.FullName); 

      // add the opening and closing tags 
      string text = "<root>" + sr.ReadToEnd() + "<root>"; 
      sr.Close(); 

      // now open the same file for writing 
      StreamWriter sw = new StreamWriter(file.FullName, false); 
      sw.Write(text); 
      sw.Close(); 
     } 

Irgendwelche Empfehlungen?

+0

Einfach nur neugierig sein: Eine Zeichenfolge hier ist schlecht für die Leistung, oder ist C# -String-Klasse gut genug dafür? – schnaader

+0

Der Hauptgrund, keine Zeichenfolge zu verwenden, besteht darin, dass sie unveränderlich ist, dh jedes Mal, wenn Sie Zeichenfolgen hinzufügen, müssen Sie ein neues String-Objekt erstellen. Da sein Code nur 2 Verkettungen hat, sehe ich keinen Vorteil in der Verwendung von StringBuilder, aber vielleicht fehlt mir etwas. Wie Earwicker bemerkte, gibt es jedoch eine bessere Methode. –

+0

Ich bin auf diese Frage gestoßen, weil ich wollte, dass meine Protokolldateien die neueste Nachricht ganz oben haben. Ich habe jetzt die Protokollierung in eine Tabelle verschoben und eine Sortierung nach DateTime .. :) –

Antwort

3

Ich kann keine wirkliche Verbesserung auf diesem ... sehen, die eine Art Bummer ist. Da es keine Möglichkeit gibt, eine Datei "zu verschieben", müssen Sie immer die Bytes in der gesamten Datei verschieben, um etwas an der Spitze einzufügen.

Sie können einen Leistungsvorteil finden, wenn Sie rohe Streams anstelle des StreamReader verwenden, der den Stream tatsächlich als Text parsen muss.

+0

Die Leistungseinbuße ist, dass die gesamte Datei in den Arbeitsspeicher geladen und dann ausgegeben wird. Der Punkt von Streams ist, einen Strom von Zeichen von einem Ort zum anderen zu bewegen und diese Art von Situationen zu vermeiden. Die Optimierungsmöglichkeit besteht darin, eine Scratch-Datei zu verwenden, um zuerst den neuen Eintrag zu schreiben, dann den alten Inhalt zu streamen und schließlich diese Datei mit dem Original auszutauschen.20mb ist nicht zu viel für RAM, aber "oder mehr" kann sein. –

15

Um zu vermeiden, dass die gesamte Datei im Speicher verbleibt, benennen Sie die ursprüngliche Datei um und öffnen Sie sie mit StreamReader. Öffnen Sie dann den ursprünglichen Dateinamen mit StreamWriter, um eine neue Datei zu erstellen.

Schreiben Sie das Präfix <root> in die Datei und kopieren Sie dann Daten in großen Stücken vom Reader zum Writer. Wenn Sie alle Daten übertragen haben, schreiben Sie den Abschluss </root> (beachten Sie den Schrägstrich, wenn Sie möchten, dass es XML ist). Schließen Sie dann beide Dateien und löschen Sie das umbenannte Original.

char[] buffer = new char[10000]; 

string renamedFile = file.FullName + ".orig"; 
File.Move(file.FullName, renamedFile); 

using (StreamReader sr = new StreamReader(renamedFile)) 
using (StreamWriter sw = new StreamWriter(file.FullName, false)) 
{ 
    sw.Write("<root>"); 

    int read; 
    while ((read = sr.Read(buffer, 0, buffer.Length)) > 0) 
     sw.Write(buffer, 0, read); 

    sw.Write("</root>"); 
} 

File.Delete(renamedFile); 
+1

Dies ist eine gute Verbesserung, aber es ist dennoch erwähnenswert, dass rohe Stream-Objekte besser abschneiden als die StreamReader/Writer-Klassen. –

3

Wenn Sie nicht wollen, dies # C tun, wäre es leicht, auf der Kommandozeile oder in einer Batch-Datei zu behandeln.

ECHO ^<root^> > outfile.xml 
TYPE temp.xml >> outfile.xml 
ECHO ^</root^> >> outfile.xml 

Dies würde davon ausgehen, dass Sie über einen vorhandenen Prozess zum Abrufen der Datendateien verfügen, in die dieser eingebunden werden könnte.

+0

Der Code zum Verwalten des Aufrufs dieser Batchdatei in das umschließende C# -Programm wäre wahrscheinlich wesentlich länger als der Code, um dasselbe direkt in C# auszuführen. –

+0

Immer vorausgesetzt, dass die Stapelverarbeitung nicht an anderer Stelle während des Abrufens der Datei hinzugefügt werden kann, Process p = Process.Start ("fixup.bat", "temp.xml"); p.WaitForExit(); Schadet mir nicht so schlecht. Das Flimmern des Befehlsfensters wird ärgerlich. –

+0

Sie müssen der Stapeldatei auch mitteilen, welche Dateien angezeigt werden sollen, was bedeutet, dass Sie die Befehlszeilenfolge mit der richtigen Quotierung erstellen müssen und Sie den Pfad zur Stapeldatei herausfinden müssen, und Sie müssen lösche die temporäre Datei (eine Zeile, wo auch immer du sie hingestellt hast). Ich bin erstaunt, dass Sie diese Option sogar in Betracht ziehen würden! Es wäre trivial, einige nette kleine Hilfsmethoden zu schreiben, die es Ihnen erlauben würden, diese Art von Stream-Verkettung elegant innerhalb von C# auszudrücken und somit überhaupt keinen Prozess-Erstellungs-Overhead zu haben. –

4

20 MB ist nicht sehr viel, aber wenn Sie es als eine Zeichenfolge lesen, wird es etwa 40 MB Speicher verwenden. Das ist auch nicht besonders viel, aber es ist Verarbeitung, die Sie nicht tun müssen. Sie können damit umgehen als rohe Bytes die Speichernutzung zu reduzieren, und Decodierung zu vermeiden und Umcodierung der Daten:

byte[] start = Encoding.UTF8.GetBytes("<root>"); 
byte[] ending = Encoding.UTF8.GetBytes("</root>"); 

byte[] data = File.ReadAllBytes(file.FullName); 

int bom = (data[0] == 0xEF) ? 3 : 0; 

using (FileStream s = File.Create(file.FullName)) { 
    if (bom > 0) { 
     s.Write(data, 0, bom); 
    } 
    s.Write(start, 0, start.Length); 
    s.Write(data, bom, data.Length - bom); 
    s.Write(ending, 0, ending.Length); 
} 

Wenn Sie die Speichernutzung viel mehr recude benötigen, eine zweite Datei zu verwenden, wie Earwicker vorgeschlagen.

Bearbeiten:
Code hinzugefügt, um BOM (Byte Order Mark) zu behandeln.

+1

"40 MB Speicher .. Das ist auch nicht besonders viel ... "* hust *. –

Verwandte Themen