如何在 Go 中逐字符读取文件

How to read a file character by character in Go

我有一些大的 json 文件要解析,我想避免一次将所有数据加载到内存中。我想要一个 function/loop 可以 return 我一次一个角色。

我发现 bufio 包中的 this example for iterating over words in a string, and the ScanRunes 函数看起来可以一次 return 一个字符。我还使用了 bufio 的 ReadRune 函数,但它大部分都在工作,但感觉这是一个非常繁重的方法。

编辑

我比较了 3 种方法。所有人都使用循环从 bufio.Reader 或 bufio.Scanner.

中提取内容
  1. bufio.Reader 上使用 .ReadRune 循环读取符文。检查了对 .ReadRune.
  2. 的调用中的错误
  3. 在扫描器上调用 .Split(bufio.ScanRunes) 后从 bufio.Scanner 读取字节。在每次迭代中调用 .Scan.Bytes,检查 .Scan 调用是否有错误。
  4. 与 #2 相同,但使用 .Textbufio.Scanner 读取文本而不是字节。我没有用 string([]runes) 连接一段符文,而是用 strings.Join([]strings, "") 连接了一段字符串以形成最终的文本块。

在一个 23 MB json 文件上每个运行 10 次的时间是:

  1. 0.65 s
  2. 2.40 s
  3. 0.97 s

所以看起来ReadRune毕竟还不错。它还会导致更小、更简洁的调用,因为每个符文都是在 1 次操作 (.ReadRune) 中获取的,而不是 2 次(.Scan.Bytes)。

此代码从输入中读取符文。不需要转换,它类似于迭代器:

package main

import (
    "bufio"
    "fmt"
    "strings"
)

func main() {
    in := `{"sample":"json string"}`

    s := bufio.NewScanner(strings.NewReader(in))
    s.Split(bufio.ScanRunes)

    for s.Scan() {
        fmt.Println(s.Text())
    }
}

只需在循环中一一阅读每个符文... See example

package main

import (
    "bufio"
    "fmt"
    "io"
    "log"
    "strings"
)

var text = `
The quick brown fox jumps over the lazy dog #1.
Быстрая коричневая лиса перепрыгнула через ленивую собаку.
`

func main() {
    r := bufio.NewReader(strings.NewReader(text))
    for {
        if c, sz, err := r.ReadRune(); err != nil {
            if err == io.EOF {
                break
            } else {
                log.Fatal(err)
            }
        } else {
            fmt.Printf("%q [%d]\n", string(c), sz)
        }
    }
}

如果只是内存大小的问题。在即将发布的版本中(真的很快),json 解码器将有令牌样式增强: 你可以在这里看到它

https://tip.golang.org/pkg/encoding/json/#Decoder.Token