【问题标题】:Does bufio.NewScanner in Golang reads the entire file in memory instead of a line each?Golang 中的 bufio.NewScanner 是否读取内存中的整个文件而不是每个文件?
【发布时间】:2016-08-11 16:52:12
【问题描述】:

我试图使用 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 读取了内存中的整个文件,并在内存中迭代?如果是这样,有没有更好的方法来逐行阅读?

【问题讨论】:

  • 否,默认缓冲区大小为 4096 字节。在更大的文件上尝试一下。

标签: file go offset seek


【解决方案1】:

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

Buffer 设置扫描时使用的初始缓冲区和最大值 扫描期间可能分配的缓冲区大小。最大值 令牌大小是 max 和 cap(buf) 中的较大者。
如果max <= cap(buf), Scan 将仅使用此缓冲区,不进行分配。

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

如果在扫描开始后调用缓冲区会发生混乱。

还有:

MaxScanTokenSize 是用于缓冲令牌的最大大小,除非 用户使用 Scan.Buffer 提供显式缓冲区。实际上 最大令牌大小可能会更小,因为缓冲区可能需要包含, 例如,换行符。

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

【讨论】:

    【解决方案2】:

    您可以增加扫描仪的缓冲区大小

    例如:-

    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
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多