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

时间:2014-07-03 20:39:54

标签: go

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

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

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

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

3 个答案:

答案 0 :(得分:47)

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

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

更大的缓冲区也有帮助,特别是对于较大的文件。在我的系统上,使用我用于测试的文件,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
        }
    }
}

和基准输出:

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

答案 1 :(得分:4)

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

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

    var readSize int
    var err error
    var count int

    buf := make([]byte, 1024)

    for {
        readSize, err = r.Read(buf)
        if err != nil {
            break
        }

        var buffPosition int
        for {
            i := bytes.IndexByte(buf[buffPosition:], '\n')
            if i == -1 || readSize == buffPosition {
                break
            }
            buffPosition += i + 1
            count++
        }
    }
    if readSize > 0 && count == 0 || count > 0 {
        count++
    }
    if err == io.EOF {
        return count, nil
    }

    return count, err
}

基准

BenchmarkIndexByteWithBuffer  2000000          653 ns/op        1024 B/op          1 allocs/op
BenchmarkBytes32k             500000          3189 ns/op       32768 B/op          1 allocs/op

答案 2 :(得分:-1)

没有比你更快的方法,因为没有关于文件有多少行的元数据。您可以通过手动查找换行符来加快速度:

func lineCount(r io.Reader) (int n, error err) {
    buf := make([]byte, 8192)

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

        for _, b := range buf[:c] {
            if b == '\n' {
                n++
            }
        }
    }

    if err == io.EOF {
        err = nil
    }
}