什么是逐行读取文本文件的最快方法?

时间:2011-11-07 13:24:58

标签: c# .net performance file-io text-files

我想逐行阅读文本文件。我想知道我是否在.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
}

8 个答案:

答案 0 :(得分:264)

要找到逐行读取文件的最快方法,您必须进行一些基准测试。我在计算机上做了一些小测试,但你不能指望我的结果适用于你的环境。

使用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),则可以使用自己的代码,但应增加缓冲区大小。

答案 1 :(得分:193)

如果您使用的是.NET 4,只需使用File.ReadLines即可完成所有操作。我怀疑它与你的相同很多,除了它也可能使用FileOptions.SequentialScan和更大的缓冲区(128似乎非常小)。

答案 2 :(得分:34)

虽然File.ReadAllLines()是读取文件的最简单方法之一,但它也是最慢的方法之一。

如果您只想阅读文件中的行而不做太多工作according to these benchmarks,那么读取文件的最快方法就是以下方法:

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

但是,如果你必须对每一行做很多事情,那么this article得出结论,最好的方法如下(如果你知道你有多少行,预先分配一个字符串[]会更快重新阅读):

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

答案 3 :(得分:9)

使用以下代码:

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

这是阅读表现的巨大差异。

这是以内存消耗为代价的,但完全值得!

答案 4 :(得分:3)

如果文件大小不大,则读取所有文件的速度会更快,然后拆分字符串:

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

答案 5 :(得分:3)

Stack Overflow问题 Is 'yield return' slower than "old school" return? 中有一个很好的主题。

它说:

  

ReadAllLines将所有行加载到内存中并返回一个   串[]。如果文件很小,一切都很好。如果文件是   如果大于内存,你的内存就会耗尽。

     另一方面,

ReadLines使用yield return返回一行   一时间有了它,您可以阅读任何大小的文件。它不会加载整体   归档到内存中。

     

假设您要查找包含单词“foo”的第一行,   然后退出使用ReadAllLines,您必须阅读整个文件   进入内存,即使第一行出现“foo”。使用ReadLines,   你只读了一行。哪一个会更快?

答案 6 :(得分:1)

如果你有足够的内存,我通过将整个文件读入memory stream,然后在其上打开一个流读取器来读取这些行,我发现了一些性能提升。只要你真的计划阅读整个文件,这可以带来一些改进。

答案 7 :(得分:1)

如果要使用现有API读取行,则无法更快。但是,读取较大的块并手动查找读缓冲区中的每个新行可能会更快。