it-swarm.com.de

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

Ich möchte eine Textdatei Zeile für Zeile lesen. Ich wollte wissen, ob ich es im Rahmen von .NET C # 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
}
294
Loren C Fortner

Um den schnellsten Weg zu finden, eine Datei zeilenweise zu lesen, müssen Sie einige Benchmarks durchführen. Ich habe einige kleine Tests auf meinem Computer durchgeführt, aber Sie können nicht erwarten, dass meine Ergebnisse auf Ihre Umgebung zutreffen.

Verwenden von StreamReader.ReadLine

Dies ist im Grunde Ihre Methode. Aus irgendeinem Grund setzen Sie die Puffergröße auf den kleinstmöglichen Wert (128). Wenn Sie dies erhöhen, wird die Leistung 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 durchführen, um eine optimale Puffergröße zu ermitteln. 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 Konstruktor FileStream können Sie FileOptions angeben. Wenn Sie beispielsweise eine große Datei nacheinander von Anfang bis Ende lesen, können Sie von _FileOptions.SequentialScan_ profitieren. Auch hier ist Benchmarking das Beste, was Sie tun können.

Verwenden von File.ReadLines

Dies ist Ihrer eigenen Lösung sehr ähnlich, mit der Ausnahme, dass sie unter Verwendung von StreamReader mit einer festen Puffergröße von 1.024 implementiert wird. Auf meinem Computer führt dies zu einer etwas besseren Leistung im Vergleich zu Ihrem Code mit der Puffergröße 128. Sie können jedoch dieselbe Leistungssteigerung erzielen, wenn Sie eine größere Puffergröße verwenden. Diese Methode wird mithilfe eines Iteratorblocks implementiert und belegt nicht für alle Zeilen Speicher.

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

Verwenden von File.ReadAllLines

Dies ist der vorherigen Methode sehr ähnlich, mit der Ausnahme, dass mit dieser Methode eine Liste von Zeichenfolgen erstellt wird, mit denen das zurückgegebene Zeilenarray erstellt wird, sodass der Speicherbedarf höher ist. Es wird jedoch _String[]_ und nicht _IEnumerable<String>_ zurückgegeben, sodass Sie zufällig 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 erheblich langsamer, zumindest bei großen Dateien (getestet mit einer 511 KB-Datei), wahrscheinlich aufgrund der Implementierung von _String.Split_. Außerdem wird allen Zeilen ein Array zugewiesen, wodurch sich der Speicherbedarf im Vergleich zu Ihrer Lösung erhöht.

_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 effizient ist. Wenn Sie spezielle Freigabeoptionen benötigen (z. B. _FileShare.ReadWrite_), können Sie Ihren eigenen Code verwenden, aber Sie sollten die Puffergröße erhöhen.

276

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

196
Jon Skeet

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

Wenn Sie nur Zeilen in einer Datei lesen möchten, ohne viel zu tun, gemäß diesen Benchmarks , ist der schnellste Weg, eine Datei zu lesen, die uralte Methode von:

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, folgt aus diesem Artikel der beste Weg (und es ist schneller, einen String [] vorab zuzuweisen, wenn Sie wissen, wie viele Zeilen Sie haben) werde lesen):

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
});
33
Free Coder 24

Verwenden Sie den folgenden Code:

foreach (string line in File.ReadAllLines(fileName))

Dies war ein großer Unterschied in der Leseleistung.

Es geht auf Kosten des Speicherverbrauchs, ist es aber absolut wert!

9
user2671536

Wenn die Datei nicht groß ist, kann die gesamte Datei schneller gelesen werden, als wenn die Zeichenfolge geteilt wird:

var filestreams = sr.ReadToEnd().Split(Environment.NewLine, 
                              StringSplitOptions.RemoveEmptyEntries);
3
Saeed Amiri

Zu diesem Thema gibt es ein gutes Thema in der Frage zum Stapelüberlauf Ist die Rendite langsamer als die Rendite der "alten Schule"?.

Es sagt:

ReadAllLines lädt alle Zeilen in den Speicher und gibt einen String [] zurück. Schön und gut, wenn die Datei klein ist. Wenn die Datei größer ist, als sie in den Arbeitsspeicher passt, ist der Arbeitsspeicher knapp.

ReadLines verwendet dagegen yield return, um jeweils eine Zeile zurückzugeben. Mit ihm können Sie Dateien jeder Größe lesen. Es wird nicht die gesamte Datei in den Speicher geladen.

Angenommen, Sie möchten die erste Zeile mit dem Wort "foo" finden und dann beenden. Mit ReadAllLines müssten Sie die gesamte Datei in den Speicher einlesen, auch wenn in der ersten Zeile "foo" auftritt. Mit ReadLines lesen Sie nur eine Zeile. Welches wäre schneller?

3
Marcel James

Wenn Sie über genügend Arbeitsspeicher verfügen, habe ich einige Leistungsverbesserungen erzielt, indem ich die gesamte Datei in einen Speicher-Stream eingelesen und anschließend einen Stream-Reader geöffnet habe, um die Zeilen zu lesen. Solange Sie tatsächlich vorhaben, die gesamte Datei zu lesen, kann dies zu Verbesserungen führen.

1
Kibbee

Sie können nicht schneller werden, wenn Sie eine vorhandene API zum Lesen der Zeilen verwenden möchten. Das Lesen größerer Chunks und das manuelle Finden jeder neuen Zeile im Lesepuffer wäre wahrscheinlich schneller.

1
jgauffin