golang:如何有效地确定文件中的行数?

Sun*_*arc 30 go

在Golang,我正在寻找一种有效的方法来确定文件的行数.

当然,我总是可以循环遍历整个文件,但似乎效率不高.

file, _ := os.Open("/path/to/filename")
fileScanner := bufio.NewScanner(file)
lineCount := 0
for fileScanner.Scan() {
    lineCount++
}
fmt.Println("number of lines:", lineCount)
Run Code Online (Sandbox Code Playgroud)

是否有更好(更快,更便宜)的方式来查找文件有多少行?

Jim*_*imB 52

这是一个更快的行计数器,bytes.Count用于查找换行符.

它更快,因为它消除了返回整行所需的所有额外逻辑和缓冲,并利用字节包提供的一些汇编优化函数来搜索字节片中的字符.

较大的缓冲区也有帮助,特别是对于较大的文件.在我的系统上,使用我用于测试的文件,32k缓冲区最快.

func lineCounter(r io.Reader) (int, error) {
    buf := make([]byte, 32*1024)
    count := 0
    lineSep := []byte{'\n'}

    for {
        c, err := r.Read(buf)
        count += bytes.Count(buf[:c], lineSep)

        switch {
        case err == io.EOF:
            return count, nil

        case err != nil:
            return count, err
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

和基准输出:

BenchmarkBuffioScan   500      6408963 ns/op     4208 B/op    2 allocs/op
BenchmarkBytesCount   500      4323397 ns/op     8200 B/op    1 allocs/op
BenchmarkBytes32k     500      3650818 ns/op     65545 B/op   1 allocs/op
Run Code Online (Sandbox Code Playgroud)

  • 我认为确实如此,我在计算字节后检查EOF (4认同)
  • 只是为了添加一些信息,我用它来计算一个1.5GB的文本文件,它在我的chromebook上大约5-10秒就读完了. (3认同)
  • 出于好奇,我做了 [一个快速的 CLI](https://gist.github.com/Hubro/76e6b7c906c308b84b23188578b74483) 来比较这个函数与 `wc -l` 的速度。我测试了一个 1.6 GB 的日志文件,它有 1160 万行。我第一次使用 `wc -l` 计算行数时,文件被缓存花了几秒钟。在那之后,`wc -l` 花费了 0.387 到 0.422 秒来计算行数。`./linecounter` 将文件计数在 0.407 到 0.425 秒之间。在我看来非常值得尊敬。 (3认同)

Dan*_*llo 7

我发现的最有效的方法是使用字节数据包的 IndexByte,它至少比使用快四倍,bytes.Count并且取决于缓冲区的大小,它使用的内存要少得多。

func LineCounter(r io.Reader) (int, error) {

    var count int
    const lineBreak = '\n'

    buf := make([]byte, bufio.MaxScanTokenSize)

    for {
        bufferSize, err := r.Read(buf)
        if err != nil && err != io.EOF {
            return 0, err
        }

        var buffPosition int
        for {
            i := bytes.IndexByte(buf[buffPosition:], lineBreak)
            if i == -1 || bufferSize == buffPosition {
                break
            }
            buffPosition += i + 1
            count++
        }
        if err == io.EOF {
            break
        }
    }

    return count, nil
}
Run Code Online (Sandbox Code Playgroud)

基准

BenchmarkIndexByteWithBuffer  2000000          653 ns/op        1024 B/op          1 allocs/op
BenchmarkBytes32k             500000          3189 ns/op       32768 B/op          1 allocs/op
Run Code Online (Sandbox Code Playgroud)