web-dev-qa-db-ja.com

テキストファイルを1行ずつ読み込むための最も早い方法は何ですか?

テキストファイルを1行ずつ読みたい。 .NET C#の範囲内で可能な限り効率的に実行しているかどうかを知りたかったのです。

これは私がこれまでに試しているものです:

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
}
279
Loren C Fortner

ファイルを1行ずつ読み込む最速の方法を見つけるには、ベンチマークをする必要があります。私は私のコンピュータでいくつかの小さなテストをしましたが、あなたは私の結果があなたの環境に当てはまるとは期待できません。

StreamReader.ReadLineを使用する

これは基本的にあなたの方法です。何らかの理由で、バッファサイズを可能な限り小さい値(128)に設定しました。これを大きくすると、一般にパフォーマンスが向上します。既定のサイズは1,024で、その他の適切な選択は512(Windowsのセクターサイズ)または4,096(NTFSのクラスターサイズ)です。最適なバッファサイズを決定するためにベンチマークを実行する必要があります。より大きなバッファは、速くはないにしても、少なくとも小さいバッファより遅くはありません。

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
  }

FileStreamコンストラクタを使用すると、 FileOptions を指定できます。たとえば、大きなファイルを最初から最後まで順番に読んでいる場合は、FileOptions.SequentialScanの恩恵を受ける可能性があります。繰り返しになりますが、ベンチマークはあなたができる最善のことです。

File.ReadLinesを使用する

これは、1,024の固定バッファサイズを持つStreamReaderを使用して実装されている点を除けば、独自のソリューションと非常によく似ています。私のコンピュータでは、これにより、バッファサイズが128のコードと比べて、パフォーマンスがわずかに向上します。ただし、より大きなバッファサイズを使用することで、同じパフォーマンスの向上を得ることができます。このメソッドはイテレータブロックを使用して実装されており、すべての行のメモリを消費するわけではありません。

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

File.ReadAllLinesを使用する

これは前の方法と非常によく似ていますが、この方法では返される行の配列を作成するために使用される文字列のリストが大きくなるため、メモリ要件が高くなります。ただし、String[]ではなくIEnumerable<String>が返されるので、ユーザーはその行にランダムにアクセスできます。

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

String.Splitを使用する

このメソッドは、少なくとも511 KBのファイルでテストされた大きなファイルではかなり遅くなります。これはおそらくString.Splitの実装方法によるものです。また、すべての行に配列を割り当てて、ソリューションと比較して必要なメモリを増やします。

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

私の提案は、 File.ReadLines を使用することです。特別な共有オプションが必要な場合(例えばFileShare.ReadWriteを使用する場合)、独自のコードを使用できますが、バッファーサイズを増やす必要があります。

258

.NET 4を使用している場合は、単純に File.ReadLines を使用してください。 much はあなたのものと同じだと思いますが、 FileOptions.SequentialScan とそれより大きいバッファ(128は非常に小さいようです)を使うかもしれません。

192
Jon Skeet

File.ReadAllLines()はファイルを読むための最も簡単な方法の一つですが、それはまた最も遅い方法の一つです。

あまりしないでファイル内の行を読みたいだけの場合は、 これらのベンチマークに従って を参照してください。ファイルを読み取る最も早い方法は、以下の古い方法です。

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

しかし、あなたが各行に多くのことをしなければならないならば、 この記事 が最善の方法は次のように結論づけます読みに行く):

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

次のコードを使用してください。

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

これは読書のパフォーマンスにおける大きな違いでした。

それはメモリ消費を犠牲にして来ます、しかしそれは全く価値があります!

9
user2671536

Stack Overflow questionにこれに関する良い話題があります。「イールドリターン」は「オールドスクール」リターンより遅いですか?

それは言います:

ReadAllLinesはすべての行をメモリにロードし、文字列[]を返します。ファイルが小さければ、すべて問題ありません。ファイルがメモリに収まるよりも大きい場合は、メモリ不足になります。

一方、ReadLinesはyield returnを使って一度に1行ずつ返します。それを使えば、どんなサイズのファイルでも読むことができます。ファイル全体をメモリにロードするわけではありません。

たとえば、 "foo"という単語を含む最初の行を見つけて終了したいとしましょう。 ReadAllLinesを使用すると、 "foo"が最初の行に表示されても、ファイル全体をメモリに読み込む必要があります。 ReadLinesでは、1行しか読みません。どちらが速いでしょうか。

3
Marcel James

ファイルサイズが大きくない場合は、すべてのファイルを読み込むのが速く、次に文字列を分割します。

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

十分なメモリがある場合は、ファイル全体を メモリストリーム に読み込み、その上でストリームリーダーを開いて行を読み込むことで、パフォーマンスが若干向上します。とにかく実際にファイル全体を読むことを計画している限り、これはいくらかの改善をもたらすことができます。

1
Kibbee

既存のAPIを使用して行を読みたい場合は、これ以上速くすることはできません。しかし、大きなチャンクを読み、手動で読み込みバッファ内の新しい各行を見つける方がおそらく速いでしょう。

1
jgauffin