【问题标题】:How to count lines fast?如何快速计算行数?
【发布时间】:2011-08-31 09:03:00
【问题描述】:

我尝试了unxutils'wc -l,但对于 1GB 文件它崩溃了。我试过这个 C# 代码

long count = 0;
using (StreamReader r = new StreamReader(f))
{
    string line;
    while ((line = r.ReadLine()) != null)
    {
        count++;
    }
}

return count;

它在 4 秒内读取一个 500MB 的文件

var size = 256;
var bytes = new byte[size];
var count = 0;
byte query = Convert.ToByte('\n');
using (var stream = File.OpenRead(file))
{
    int many;
    do
    {
        many = stream.Read(bytes, 0, size);
        count += bytes.Where(a => a == query).Count();                    
    } while (many == size);
}

10 秒内阅读

var count = 0;
int query = (int)Convert.ToByte('\n');
using (var stream = File.OpenRead(file))
{
    int current;
    do
    {
        current = stream.ReadByte();
        if (current == query)
        {
            count++;
            continue;
        }
    } while (current!= -1);
}

需要 7 秒

有什么更快的方法我还没试过?

【问题讨论】:

  • @nCdy 添加为答案
  • 您的个人资料运行时发现哪些是热点?
  • 您确定要测试行数而不是文件系统吗?如果第一个测试加载文件以便缓存它,那么后续测试将运行得更快。确保你真的在测试你认为你在测试的东西。
  • +1 给吉姆·米歇尔。性能测试比大多数人想象的要复杂!
  • 这里有一些讨论:bytes.com/topic/c-sharp/answers/…

标签: c# streamreader wc


【解决方案1】:

File.ReadLines 是在 .NET 4.0 中引入的

var count = File.ReadLines(file).Count();

4秒内工作,与第一个代码sn-p时间相同

【讨论】:

  • 那是因为它基本上和你的第一个 sn-p 做同样的事情;)
  • 从不使用 Count(),使用 Length (File.ReadAllLines(@"yourfile").Length;) // 再次检查此解决方案,但使用 Length
  • @nCdy:这是一个非常糟糕的建议(在这种情况下)!注意区别:他使用的是File.ReadLines(),它实际上返回一个IEnumerable<string>,并且只是做了一个yield return,这基本上是他的第一个sn-p所做的。 File.ReadAllLines() 会将 所有行 读入内存,这在性能方面会很糟糕。当然,如果你确实已经有一个数组,你应该使用Length 而不是Count() ;)
  • @SirViver 同意了。他不需要加载所有行,但如果他不会使用它们。
  • @nCdy 正如 SirViver 所说的Exception of type 'System.OutOfMemoryException' was thrown.
【解决方案2】:

您的第一种方法看起来已经是最佳解决方案了。请记住,您主要不受 CPU 限制,而是受到 HD 读取速度的限制,500MB / 4sec = 125MB/s 已经相当快了。获得比这更快的唯一方法是通过 RAID 或使用 SSD,而不是通过更好的算法。

【讨论】:

  • 我还发现我可以通过获取文件大小并将其除以第一行的中等大小来估计行数。
  • @JaderDias:是的,但是您只有一个估计值,而不是实际计数。根据文件的结构,您的估计最终可能会相差远。您没有指定行计数的目的是什么或文件通常是什么样子,因此无法真正给出更专业的建议。
  • 对于我的 CSV 文件,估算值足够准确
【解决方案3】:

您是否只是在寻找一种工具来高效地计算文件中的行数?如果是这样,请尝试 MSLogParser

类似下面的东西会给你行数:

LogParser "SELECT count(*) FROM file" -i:TEXTLINE

【讨论】:

    【解决方案4】:

    如果您真的想要快速,请考虑使用 C 代码。

    如果这是一个命令行实用程序,它会更快,因为它不必初始化 CLR 或 .NET。而且,它不会为从文件中读取的每一行重新分配一个新字符串,这可能会节省吞吐量时间。

    我没有任何 1g 行的文件,所以我无法比较。不过你可以试试:

    /*
     * LineCount.c
     *
     * count lines...
     *
     * compile with: 
     *
     *  c:\vc10\bin\cl.exe /O2 -Ic:\vc10\Include -I\winsdk\Include 
     *          LineCount.c -link /debug /SUBSYSTEM:CONSOLE /LIBPATH:c:\vc10\Lib
     *          /LIBPATH:\winsdk\Lib /out:LineCount.exe
     */
    
    #include <stdio.h>
    #include <string.h>
    #include <stdlib.h>
    
    
    void Usage(char *appname)
    {
        printf("\nLineCount.exe\n");
        printf("  count lines in a text file...\n\n");
        printf("usage:\n");
        printf("  %s <filename>\n\n", appname);
    }
    
    
    
    int linecnt(char *file)
    {
        int sz = 2048;
        char *buf = (char *) malloc(sz);
        FILE *fp = NULL;
        int n= 0;
        errno_t rc = fopen_s(&fp, file, "r");
    
        if (rc) {
            fprintf(stderr, "%s: fopen(%s) failed: ecode(%d)\n",
                    __FILE__, file, rc);
            return -1;
        }
    
        while (fgets(buf, sz, fp)){
            int r = strlen(buf);
            if (buf[r-1] == '\n')
                n++;
            // could re-alloc here to handle larger lines
        }
        fclose(fp);
        return n;
    }
    
    int main(int argc, char **argv)
    {
        if (argc==2) {
            int n = linecnt (argv[1]);
            printf("Lines: %d\n", n);
        }
        else {
            Usage(argv[0]);
            exit(1);
        }
    }
    

    【讨论】:

    • 可能更快,但我敢打赌差异小于 10%
    • 试试看。知道会很有趣。
    • 10 秒 =( 与所有其他测试一样在 VS2010 上运行调试
    • @Jader:哇 等一下,您正在调试模式下运行性能测试? 从来没有这样做过。你会得到完全误导的结果。调试器会故意对您的程序进行反优化以改善调试体验。在这种情况下,这可能不是问题,因为您受磁盘限制,而不是处理器限制,但在调试器中测量性能仍然是一种可怕的编程实践。
    • @Jader:就像我说的,那是因为你很幸运,碰巧选择了一个受磁盘硬件速度限制的性能问题。当您尝试优化受实际代码速度限制的东西时,情况就完全不同了。
    【解决方案5】:

    我认为你的答案看起来不错。我唯一要添加的是使用缓冲区大小。我觉得它可能会根据您的缓冲区大小改变性能。

    请参考缓冲区大小 - Optimum file buffer read size?

    【讨论】:

    • 我尝试了不同的值,任何高于 256 的值都将具有相同的性能,而较低的值(如 4)则速度较慢。
    【解决方案6】:

    你试过 flex 吗?

    %{
    long num_lines = 0;
    %}
    %option 8bit outfile="scanner.c"
    %option nounput nomain noyywrap
    %option warn
    
    %%
    .+ { }
    \n { ++num_lines; }
    %%
    int main(int argc, char **argv);
    
    int main (argc,argv)
    int argc;
    char **argv;
    {
    yylex();
    printf( "# of lines = %d\n", num_lines );
    return 0;
    }
    

    只需编译:

    flex -Cf scanner.l 
    gcc -O -o lineCount.exe scanner.c
    

    它接受标准输入的输入并输出行数。

    【讨论】:

      猜你喜欢
      • 2016-02-21
      • 1970-01-01
      • 2018-10-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-01-15
      • 1970-01-01
      • 2020-12-08
      相关资源
      最近更新 更多