file - 用小 RAM 在 Go 中读取大文件的最快方法
问题描述
我想从不同的文本或JSON或CSV文件中读取数据。我应该遵循哪种方法?
我有博客文章File read and Read 2 GB text file with small RAM,用于文件读取的不同方法。
不同的方法:
- 分块读取文件
- 同时读取文件块
- 将整个文件读入内存
- 将长字符串拆分为单词
- 逐字扫描
用小 RAM 读取文件的最快方法是什么?
解决方案
解析文件基本上有两种不同的方法:文档解析和流解析。
文档解析从文件中读取数据并将其转换为您可以查询的大量对象,例如浏览器中的 HTML DOM。优点是您可以轻松获得完整的数据,这通常更简单。缺点是您必须将其全部存储在内存中。
dom = parse(stuff)
// now do whatever you like with the dom
相反,流解析一次读取一个元素并将其呈现给您以供立即使用,然后继续处理下一个元素。
for element := range stream(stuff) {
...examine one element at a time...
}
优点是您不必将整个内容加载到内存中。缺点是您必须处理经过的数据。这对于搜索或其他需要一一处理的内容非常有用。
幸运的是,Go 提供了库来为您处理常见格式。
一个简单的示例是处理 CSV 文件。
package main
import(
"encoding/csv"
"fmt"
"log"
"os"
"io"
)
func main() {
file, err := os.Open("test.csv")
if err != nil {
log.Fatal(err)
}
parser := csv.NewReader(file)
...
}
我们可以将整个事情作为一个大的[][]string
.
records, err := parser.ReadAll()
if err != nil {
log.Fatal(err)
}
for _,record := range records {
fmt.Println(record)
}
或者我们可以节省一堆内存并一次处理一行。
for {
record, err := parser.Read()
if err == io.EOF {
break
}
if err != nil {
log.Fatal(err)
}
fmt.Println(record)
}
由于 CSV 的每一行在功能上都是相同的,因此一次处理一行是最有意义的。
JSON 和 XML 更复杂,因为它们是大型的嵌套结构,但它们也可以流式传输。encoding/json 文档中有一个流式传输的示例。
如果你的代码不是一个简单的循环怎么办?如果您想利用并发性怎么办?使用通道和 goroutine 与程序的其余部分同时提供它。
records := make( chan []string )
go func() {
parser := csv.NewReader(file)
defer close(records)
for {
record, err := parser.Read()
if err == io.EOF {
break
}
if err != nil {
log.Fatal(err)
}
records <- record
}
}();
现在您可以传递records
给可以处理它们的函数。
func print_records( records chan []string ) {
for record := range records {
fmt.Println(record)
}
}
推荐阅读
- android - 访问和共享内部/外部存储Android Q中图片文件夹下的文件
- python-3.x - 线程化参数列表并在 Python 中加入结果
- java - 毕加索图片加载不出来
- c# - TextBox.Enter() 事件在 WinForm 中触发 Textbox.Leave() 事件
- javascript - 如何在 React v4 中创建自定义钩子 useHistory
- ansible - Ansible 循环插入
- jquery - 获取动态数据属性
- kubernetes - 如何使用侧车部署 Grafana 仪表板并将仪表板与特定仪表板文件夹分组
- android - Android 静态片段:当布局高度为 wrap_content 时,文本无法正确显示
- java - 如何使用 Java 中的功能标记为不同的环境启用不同的配置