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

Lor*_*ner 304 .net c# 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
}
Run Code Online (Sandbox Code Playgroud)

Mar*_*age 296

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

使用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
  }
Run Code Online (Sandbox Code Playgroud)

FileStream构造函数允许你指定FileOptions.例如,如果您从头到尾依次读取大文件,则可能会从中受益FileOptions.SequentialScan.再次,基准测试是您可以做的最好的事情.

使用File.ReadLines

这非常类似于您自己的解决方案,除了它是使用StreamReader固定缓冲区大小为1,024的实现.在我的计算机上,与缓冲区大小为128的代码相比,性能稍好一些.但是,通过使用更大的缓冲区大小,可以获得相同的性能提升.此方法使用迭代器块实现,不消耗所有行的内存.

var lines = File.ReadLines(fileName);
foreach (var line in lines)
  // Process line
Run Code Online (Sandbox Code Playgroud)

使用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
}
Run Code Online (Sandbox Code Playgroud)

使用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
}
Run Code Online (Sandbox Code Playgroud)

我的建议是使用,File.ReadLines因为它干净,高效.如果您需要特殊的共享选项(例如您使用FileShare.ReadWrite),您可以使用自己的代码但是应该增加缓冲区大小.

  • 我不能说速度性能但有一点是肯定的:它在内存消耗方面要糟糕得多.如果必须处理非常大的文件(例如GB),这非常关键.如果它意味着它必须交换内存,甚至更多.在速度方面,您可以添加ReadAllLine需要在返回结果延迟处理之前读取所有行.在某些情况下,速度的IMPRESSION比原始速度更重要. (2认同)

Jon*_*eet 197

如果您使用的是.NET 4,只需使用File.ReadLines哪种功能即可.我怀疑这是很多和你一样,但它也可以使用FileOptions.SequentialScan和更大的缓冲区(128看起来非常小).


Fre*_* 24 33

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

如果你只是想在文件中读取行而不做太多,根据这些基准测试,读取文件的最快方法是使用古老的方法:

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

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

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
});
Run Code Online (Sandbox Code Playgroud)


小智 11

使用以下代码:

foreach (string line in File.ReadAllLines(fileName))
Run Code Online (Sandbox Code Playgroud)

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

它以内存消耗为代价,但完全值得!

  • 我更喜欢 [File.ReadLines(点击我)](https://medium.com/@nuno.caneco/c-file-readlines-vs-file-readalllines-4742d738064e) 而不是 `File.ReadAllLines` (2认同)

Mar*_*mes 6

在 Stack Overflow 问题“收益回报”比“老派”回报慢吗?.

它说:

ReadAllLines 将所有行加载到内存中并返回一个 string[]。如果文件很小,一切都很好。如果文件大于内存容量,则会耗尽内存。

另一方面,ReadLines 使用 yield return 一次返回一行。有了它,您可以读取任何大小的文件。它不会将整个文件加载到内存中。

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


Sae*_*iri 5

如果文件不大,那么读取整个文件然后再分割它会更快

var filestreams = sr.ReadToEnd().Split(Environment.NewLine, 
                              StringSplitOptions.RemoveEmptyEntries);
Run Code Online (Sandbox Code Playgroud)