首页 文章

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

提问于
浏览
262

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

9 回答

  • 3

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

    Using 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 . 再次,基准测试是您可以做的最好的事情 .

    Using File.ReadLines

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

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

    Using 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
    }
    

    Using 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

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

  • 6

    虽然 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得出结论,最好的方法是以下(并且's faster to pre-allocate a string[] if you know how many lines you'将要阅读):

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

    使用以下代码:

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

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

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

  • 2

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

    它说:

    ReadAllLines将所有行加载到内存中并返回字符串[] . 如果文件很小,一切都很好 . 如果文件大于适合内存的文件,则内存不足 . 另一方面,ReadLines使用yield return一次返回一行 . 有了它,您可以阅读任何大小的文件 . 它不会将整个文件加载到内存中 . 假设您要查找包含单词“foo”的第一行,然后退出 . 使用ReadAllLines,您必须将整个文件读入内存,即使第一行出现“foo” . 使用ReadLines,您只能阅读一行 . 哪一个会更快?

  • 30

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

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

    如果你的目标是执行速度,那么你是 . 使用StreamReader构造函数可以缩短代码 .

  • 229

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

  • 190

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

相关问题