Golang中的bufio.NewScanner是否会将整个文件读入内存而不是逐行读取?

4

我试图使用以下函数和 bufio.NewScanner 按行读取文件。

func TailFromStart(fd *os.File, wg *sync.WaitGroup)  {

    fd.Seek(0,0)
    scanner := bufio.NewScanner(fd)
    for scanner.Scan() {
        line := scanner.Text()
        offset, _ := fd.Seek(0, 1)
        fmt.Println(offset)
        fmt.Println(line)
        offsetreset, _ := fd.Seek(offset, 0)
        fmt.Println(offsetreset)
    }
    offset, err := fd.Seek(0, 1)
    CheckError(err)
    fmt.Println(offset)
    wg.Done()

}

我本以为它会按照递增顺序打印偏移量,但是直到文件到达 EOF,它才在每次迭代中打印相同的值。

127.0.0.1 - - [11/Aug/2016:22:10:39 +0530] "GET /ttt HTTP/1.1" 404 437 "-" "curl/7.38.0"
613
613
127.0.0.1 - - [11/Aug/2016:22:10:42 +0530] "GET /qqq HTTP/1.1" 404 437 "-" "curl/7.38.0"
613

613 是该文件中所有字符的总数。

cat /var/log/apache2/access.log | wc
  7      84     613

我是否理解错误,bufio.NewScanner是否将整个文件读入内存,然后在内存中进行迭代?如果是这样,有没有更好的方法逐行读取?


4
不,缺省的缓冲区大小为4096字节。请试着使用一个更大的文件进行测试。 - JimB
2个回答

4

请查看func (s *Scanner) Buffer(buf []byte, max int)文档:

Buffer方法用于设置扫描时使用的初始缓冲区和最大缓冲区大小。最大令牌大小为max和cap(buf)中的较大值。
如果max <= cap(buf),则Scan将仅使用此缓冲区而不进行分配。

默认情况下,Scan使用内部缓冲区,并将最大令牌大小设置为MaxScanTokenSize

如果在扫描开始后调用Buffer,将会引发panic。

另外:

MaxScanTokenSize is the maximum size used to buffer a token unless the user provides an explicit buffer with Scan.Buffer. The actual maximum token size may be smaller as the buffer may need to include, for instance, a newline.

MaxScanTokenSize = 64 * 1024

startBufSize = 4096 // Size of initial allocation for buffer.
不, 如 @JimB 所说,它只读取缓冲区大小,看这个测试样例:
对于小于4096个字节的文件,它会将所有内容读入缓冲区,
但对于大文件只读取前4096个字节,
请用大文件尝试此操作:
package main

import (
    "bufio"
    "fmt"
    "os"
)

func main() {
    fd, err := os.Open("big.txt")
    if err != nil {
        panic(err)
    }
    defer fd.Close()

    n, err := fd.Seek(0, 0)
    if err != nil {
        panic(err)
    }
    fmt.Println("n =", n) // 0

    scanner := bufio.NewScanner(fd)
    for scanner.Scan() {
        fmt.Println(scanner.Text())
        break
    }

    offset, err := fd.Seek(0, 1)
    if err != nil {
        panic(err)
    }
    fmt.Println("offset =", offset) //4096

    offsetreset, err := fd.Seek(offset, 0)
    if err != nil {
        panic(err)
    }
    fmt.Println("offsetreset =", offsetreset) //4096

    offset, err = fd.Seek(0, 1)
    if err != nil {
        panic(err)
    }
    fmt.Println("offset =", offset) //4096

}

输出:

n = 0

offset = 4096
offsetreset = 4096
offset = 4096

1
你可以增加扫描器的缓冲区大小。
例如:
scanner := bufio.NewScanner(file)
buf := make([]byte, 0, 64*1024)
scanner.Buffer(buf, 1024*1024) //1024*1024 => 1mb max (you can change value here to read larger files
for scanner.Scan() {
    // do your stuff
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接