Was ist der schnellste Weg, eine Textdatei Zeile für Zeile zu lesen?

Ich möchte Zeile für Zeile eine Textdatei lesen. Ich wollte wissen, ob ich es innerhalb des .NET C # -Spektrums so effizient wie möglich mache.

Das versuche ich bisher:

var filestream = new System.IO.FileStream(textFilePath, System.IO.FileMode.Open, System.IO.FileAccess.Read, System.IO.FileShare.ReadWrite); var file = new System.IO.StreamReader(filestream, System.Text.Encoding.UTF8, true, 128); while ((lineOfText = file.ReadLine()) != null) { //Do something with the lineOfText } 

Um den schnellsten Weg zu finden, eine Datei Zeile für Zeile zu lesen, müssen Sie ein Benchmarking durchführen. Ich habe einige kleine Tests an meinem Computer durchgeführt, aber Sie können nicht erwarten, dass meine Ergebnisse für Ihre Umgebung gelten.

Verwenden von StreamReader.ReadLine

Dies ist im Grunde Ihre Methode. Aus irgendeinem Grund setzen Sie die Puffergröße auf den kleinsten möglichen Wert (128). Wenn Sie dies erhöhen, wird die performance im Allgemeinen erhöht. Die Standardgröße ist 1.024 und andere gute Optionen sind 512 (die Sektorgröße in Windows) oder 4.096 (die Clustergröße in NTFS). Sie müssen einen Benchmark ausführen, um eine optimale Puffergröße zu bestimmen. Ein größerer Puffer ist – wenn nicht schneller – zumindest nicht langsamer als ein kleinerer Puffer.

 const Int32 BufferSize = 128; using (var fileStream = File.OpenRead(fileName)) using (var streamReader = new StreamReader(fileStream, Encoding.UTF8, true, BufferSize)) { String line; while ((line = streamReader.ReadLine()) != null) // Process line } 

Mit dem FileStream Konstruktor können Sie FileOptions angeben. Wenn Sie beispielsweise eine große Datei sequenziell von Anfang bis Ende lesen, profitieren Sie möglicherweise von FileOptions.SequentialScan . Auch hier ist Benchmarking das Beste, was Sie tun können.

Verwenden von File.ReadLines

Dies ist Ihrer eigenen Lösung sehr ähnlich, außer dass sie mit einem StreamReader mit einer festen Puffergröße von 1.024 implementiert wird. Auf meinem Computer führt dies zu einer etwas besseren performance im Vergleich zu Ihrem Code mit der Puffergröße 128. Sie können jedoch die gleiche performancessteigerung erzielen, indem Sie eine größere Puffergröße verwenden. Diese Methode wird mithilfe eines Iteratorblocks implementiert und belegt nicht den gesamten Speicher für alle Zeilen.

 var lines = File.ReadLines(fileName); foreach (var line in lines) // Process line 

Verwenden von File.ReadAllLines

Dies ähnelt sehr der vorherigen Methode, mit der Ausnahme, dass diese Methode eine Liste von Strings erstellt, die zum Erstellen des zurückgegebenen Linienarrays verwendet werden, sodass die Speicheranforderungen höher sind. Es gibt jedoch String[] und kein IEnumerable , mit dem Sie nach dem Zufallsprinzip auf die Zeilen zugreifen können.

 var lines = File.ReadAllLines(fileName); for (var i = 0; i < lines.Length; i += 1) { var line = lines[i]; // Process line } 

Verwenden von String.Split

Diese Methode ist wesentlich langsamer, zumindest bei großen Dateien (getestet in einer 511 KB großen Datei), wahrscheinlich aufgrund der Implementierung von String.Split . Es weist auch ein Array für alle Zeilen zu, wodurch der erforderliche Speicher im Vergleich zu Ihrer Lösung erhöht wird.

 using (var streamReader = File.OpenText(fileName)) { var lines = streamReader.ReadToEnd().Split("\r\n".ToCharArray(), StringSplitOptions.RemoveEmptyEntries); foreach (var line in lines) // Process line } 

Mein Vorschlag ist, File.ReadLines zu verwenden, weil es sauber und leistungsfähig ist. Wenn Sie spezielle Freigabeoptionen benötigen (zB FileShare.ReadWrite ), können Sie Ihren eigenen Code verwenden, aber Sie sollten die Puffergröße erhöhen.

Wenn Sie .NET 4 verwenden, verwenden Sie einfach File.ReadLines das alles für Sie erledigt. Ich vermute, es ist FileOptions.SequentialScan dasselbe wie deines, außer dass es auch FileOptions.SequentialScan und einen größeren Puffer (128 scheint sehr klein) verwendet.

Während File.ReadAllLines() eine der einfachsten Möglichkeiten zum Lesen einer Datei ist, ist sie auch eine der langsamsten.

Wenn Sie nur Zeilen in einer Datei lesen möchten, ohne viel zu tun, ist der schnellste Weg zum Lesen einer Datei nach diesen Benchmarks die uralte Methode:

 using (StreamReader sr = File.OpenText(fileName)) { string s = String.Empty; while ((s = sr.ReadLine()) != null) { //do minimal amount of work here } } 

Wenn Sie jedoch mit jeder Zeile viel zu tun haben, kommt dieser Artikel zu dem Schluss, dass der beste Weg der folgende ist (und es ist schneller, eine Zeichenfolge [] vorab zuzuweisen, wenn Sie wissen, wie viele Zeilen Sie lesen werden):

 AllLines = new string[MAX]; //only allocate memory here using (StreamReader sr = File.OpenText(fileName)) { int x = 0; while (!sr.EndOfStream) { AllLines[x] = sr.ReadLine(); x += 1; } } //Finished. Close the file //Now parallel process each line in the file Parallel.For(0, AllLines.Length, x => { DoYourStuff(AllLines[x]); //do your work here }); 

Verwenden Sie den folgenden Code:

 var lines = File.ReadAllLines(fileName); foreach (var line in lines) 

Dies war ein großer Unterschied in der Leseleistung.

Es kommt auf Kosten des Speicherverbrauchs, aber es lohnt sich!

Es gibt ein gutes Thema in der Stack Overflow-Frage. Ist die Rendite langsamer als die Rendite der “alten Schule”? .

Es sagt:

ReadAllLines lädt alle Zeilen in den Speicher und gibt eine Zeichenfolge [] zurück. Alles gut und gut, wenn die Datei klein ist. Wenn die Datei größer ist als in den Arbeitsspeicher passt, ist nicht genügend Speicher verfügbar.

ReadLines dagegen verwendet die Rendite-Rückgabe, um jeweils eine Zeile zurückzugeben. Damit können Sie jede Datei mit beliebiger Größe lesen. Es lädt nicht die gesamte Datei in den Speicher.

Angenommen, Sie möchten die erste Zeile mit dem Wort “foo” suchen und dann beenden. Wenn Sie ReadAllLines verwenden, müssen Sie die gesamte Datei im Speicher ablegen, auch wenn “foo” in der ersten Zeile erscheint. Mit ReadLines lesen Sie nur eine Zeile. Welcher wäre schneller?

Wenn die Dateigröße nicht groß ist, ist es schneller, alle Dateien zu lesen und dann die Zeichenfolge zu teilen:

 var filestreams = sr.ReadToEnd().Split("\r\n".ToCharArray(), StringSplitOptions.RemoveEmptyEntries); 

Wenn Sie die Ausführungsgeschwindigkeit anstreben, ja, sind Sie. Der Code könnte kürzer sein, wenn Sie den StreamReader-Konstruktor verwenden.

Wenn Sie genügend Speicher haben, habe ich einige performancessteigerungen gefunden, indem ich die gesamte Datei in einen Speicherstream gelesen habe und dann einen Stream-Reader geöffnet habe, um die Zeilen zu lesen. So lange Sie tatsächlich vorhaben, die gesamte Datei zu lesen, kann dies zu einigen Verbesserungen führen.

Sie können nicht schneller sein, wenn Sie eine vorhandene API zum Lesen der Zeilen verwenden möchten. Aber größere Abschnitte zu lesen und jede neue Zeile im Lesepuffer manuell zu finden, wäre wahrscheinlich schneller.