代码之家  ›  专栏  ›  技术社区  ›  Reto Meier

计算文本文件中的行数有什么诀窍吗?[关闭]

  •  8
  • Reto Meier  · 技术社区  · 15 年前

    假设您有一个文本文件,那么确定该文件中文本行数的最快和/或最节省内存的方法是什么?

    这仅仅是一个逐字扫描和寻找换行符的问题吗?

    7 回复  |  直到 15 年前
        1
  •  11
  •   Matthew Whited    15 年前

    可能不是最快的,但它将是最通用的…

    int lines = 0;
    /* if you need to use an encoding other than UTF-8 you way want to try...
       new StreamReader("filename.txt", yourEncoding) 
       ... instead of File.OpenText("myFile.txt")
    */
    using (var fs = File.OpenText("myFile.txt"))
        while (!fs.EndOfStream)
        {
            fs.ReadLine();
            lines++;
        }
    

    ……这可能会更快…

    如果你需要更高的速度,你可以尝试 Duff's device 在分支之前检查10或20个字节

    int lines = 0;
    var buffer = new byte[32768];
    var bufferLen = 1;    
    using (var fs = File.OpenRead("filename.txt"))
        while (bufferLen > 0)
        {
            bufferLen = fs.Read(buffer, 0, 32768);
            for (int i = 0; i < bufferLen; i++)
                /* this is only known to work for UTF-8/ASCII other 
                   file types may need to search for different End Of Line 
                   characters */                
                if (buffer[i] == 10)           
                    lines++;
        }
    
        2
  •  10
  •   Community CDub    7 年前

    除非你有固定的线路长度(根据 字节 )您肯定需要读取数据。是否可以避免将所有数据转换为文本取决于编码。

    现在,最有效的方法将是Reinier手动计算行尾。但是, 最简单的 代码将被使用 TextReader.ReadLine() . 事实上,最简单的方法就是 LineReader 类从 MiscUtil 将文件名(或其他各种内容)转换为 IEnumerable<string> . 然后您可以使用LINQ:

    int lines = new LineReader(filename).Count();
    

    (如果你不想抓住整个MiscUtil,你可以 直线加速器 自己从 this answer )

    现在这将产生大量的垃圾,重复读取同一个char数组是不会的,但一次读取的行数不会超过一行,所以当您稍微强调gc时,它不会被大文件炸毁。它还需要将所有数据解码为文本-您 可以 能不费吹灰之力逃走 一些 编码。

    就我个人而言,这是我一直使用的代码,直到我发现它造成了一个瓶颈——要比手动操作要简单得多。你确定吗 知道 在您当前的情况下,像上面这样的代码会成为瓶颈吗?

    像以前一样,在你必须…在以后的日子里,你可以很容易地优化它,而不需要改变你的总体设计,所以推迟它不会有任何伤害。

    编辑:将Matthew的答案转换成一个对任何编码都有效的答案——但是这会导致对所有数据进行解码的惩罚,当然,您最终可能会得到类似下面代码的结果。我假设你 只有 关心 \n -而不是 \r , \ N号 \r\n 哪一个 TextReader 通常处理:

    public static int CountLines(string file, Encoding encoding)
    {
        using (TextReader reader = new StreamReader(file, encoding))
        {
            return CountLines(reader);
        }
    }
    
    public static int CountLines(TextReader reader)
    {
        char[] buffer = new char[32768];
    
        int charsRead;
        int count = 0;
    
        while ((charsRead = reader.Read(buffer, 0, buffer.Length)) > 0)
        {
            for (int i = 0; i < charsRead; i++)
            {
                if (buffer[i] == '\n')
                {
                    count++;
                }
            }
        }
        return count;
    }
    
        3
  •  5
  •   TLiebe    15 年前

    如果这是一个固定的记录,你可以得到一个记录的大小,然后将总的文件大小除以这个值,得到记录的数量。如果您只是在寻找一个估计值,我过去所做的就是读取前x行(例如200行),然后使用它得出一个平均行大小,然后您可以使用它来猜测记录总数(将总文件大小除以平均行大小)。如果你的记录是相当统一的,而且你不需要精确的计数,这就很好了。我在大型文件上使用过这个(快速检查以获取文件大小,如果超过20MB,则获取估计值,而不是读取整个文件)。

    除此之外,唯一100%准确的方法是使用readline逐行浏览文件。

        4
  •  3
  •   Toad    15 年前

    我会一次读取32kb(或更多),计算内存块中的\r\n个数,然后重复直到完成。

        5
  •  2
  •   Guffa    15 年前

    最简单的:

    int lines = File.ReadAllLines(fileName).Length;
    

    当然,这会将所有文件读取到内存中,因此它根本就没有内存效率。内存效率最高的是将文件作为流读取并查找换行符。这也是最快的,因为这是最低的开销。

    没有可以使用的快捷方式。文件不是基于行的,因此没有可以使用的额外信息,其中一种方法必须读取和检查文件的每个字节。

        6
  •  1
  •   Emilio M Bumachar    15 年前

    我相信Windows使用两个字符来标记行尾(如果我记得正确,则为10h和13h),因此您只需检查这两个字符的每一秒。

        7
  •  1
  •   deepsnore    15 年前

    因为这是一个完全连续的过程,位置之间没有依赖关系,所以如果数据真的很大,请考虑映射/减少。在C/C++中,可以使用OpenMP进行并行处理。每个线程将读取一个块并计算该块中的CRLF。最后,在reduce部分,它们将对各自的计数进行求和。英特尔线程构建块为C++提供了基于模板的并行结构。我同意这是一种针对小文件的重锤式方法,但从纯粹的性能角度来看,这是最佳方法(分而治之)