如何在 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.
中提取内容
- 在
bufio.Reader
上使用 .ReadRune
循环读取符文。检查了对 .ReadRune
. 的调用中的错误
- 在扫描器上调用
.Split(bufio.ScanRunes)
后从 bufio.Scanner
读取字节。在每次迭代中调用 .Scan
和 .Bytes
,检查 .Scan
调用是否有错误。
- 与 #2 相同,但使用
.Text
从 bufio.Scanner
读取文本而不是字节。我没有用 string([]runes)
连接一段符文,而是用 strings.Join([]strings, "")
连接了一段字符串以形成最终的文本块。
在一个 23 MB json 文件上每个运行 10 次的时间是:
0.65 s
2.40 s
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 解码器将有令牌样式增强:
你可以在这里看到它
我有一些大的 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.
中提取内容- 在
bufio.Reader
上使用.ReadRune
循环读取符文。检查了对.ReadRune
. 的调用中的错误
- 在扫描器上调用
.Split(bufio.ScanRunes)
后从bufio.Scanner
读取字节。在每次迭代中调用.Scan
和.Bytes
,检查.Scan
调用是否有错误。 - 与 #2 相同,但使用
.Text
从bufio.Scanner
读取文本而不是字节。我没有用string([]runes)
连接一段符文,而是用strings.Join([]strings, "")
连接了一段字符串以形成最终的文本块。
在一个 23 MB json 文件上每个运行 10 次的时间是:
0.65 s
2.40 s
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 解码器将有令牌样式增强: 你可以在这里看到它