怎么选择 Go 文件读取方案

文件处理是一个常见的问题,同时 Go 又提供了非常多的文件读取方法,容易让人患选择困难症。之前我们转过一篇超全总结:Go 读文件的 10 种方法的文章,列举了10 余种读取方式。本文作为其扩展,以实际不同大小的文件为例,来具体比较下它们的差异。

创建不同大小的文件

首先,我们需要有比较对象。鉴于电脑磁盘空间有限,本文就比较 KB、MB、GB 三个级别的文件读取差异。

package mainimport ( "bufio" "math/rand" "os" "time")const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"var seededRand = rand.New(rand.NewSource(time.Now().UnixNano()))func StringWithCharset(length int) string { b := make([]byte, length) for i := range b {  b[i] = charset[seededRand.Intn(len(charset))] } return string(b)}func main() { files := map[string]int{"4KB.txt": 4, "4MB.txt": 4096, "4GB.txt": 4194304, "16GB.txt": 16777216} for name, number := range files {  file, err := os.OpenFile(name, os.O_WRONLY|os.O_CREATE, 0666)  if err != nil {   panic(err)  }  write := bufio.NewWriter(file)  for i := 0; i 

执行以上代码,我们依次得到 4KB、4MB、4GB、16GB 大小的文件,它们是由每行 1KB 大小随机字符串的内容组成。

$ ls -alh 4kb.txt 4MB.txt 4GB.txt 16GB.txt-rw-r--r--  1 slp  staff    16G Mar  6 15:57 16GB.txt-rw-r--r--  1 slp  staff   4.0G Mar  6 15:54 4GB.txt-rw-r--r--  1 slp  staff   4.0M Mar  6 15:53 4MB.txt-rw-r--r--  1 slp  staff   4.0K Mar  6 15:16 4kb.txt

登录后复制

接下来,我们使用不同的方式来读取这些文件内容。

整个文件加载

Go 提供了可一次性读取文件内容的方法:os.ReadFile 与 ioutil.ReadFile。在 Go 1.16 开始,ioutil.ReadFile 就等价于 os.ReadFile。

func BenchmarkOsReadFile4KB(b *testing.B) { for i := 0; i 

一次性加载文件的优缺点非常明显,它能减少 IO 次数,但它会将文件内容都加载至内存中,对于大文件,存在内存撑爆的风险。

逐行读取

在很多情况下,例如日志分析,对文件的处理都是按行进行的。Go 中 bufio.Reader 对象提供了一个 ReadLine() 方法,但其实我们更多地是使用 ReadBytes('') 或者 ReadString('') 代替。

// ReadLine is a low-level line-reading primitive. Most callers should use// ReadBytes('') or ReadString('') instead or use a Scanner.

登录后复制

我们以 ReadString('') 为例,对 4 个文件分别进行逐行读取

func ReadLines(filename string) { fi, err := os.Open(filename) if err != nil{  panic(err) } defer fi.Close() reader := bufio.NewReader(fi) for {  _, err = reader.ReadString('')  if err != nil {   if err == io.EOF {    break   }   panic(err)  } }}func BenchmarkReadLines4KB(b *testing.B) { for i := 0; i 

块读取

块读取也称为分片读取,这也很好理解,我们可以将内容分成一块块的,每次读取指定大小的块内容。这里,我们将块大小设置为 4KB。

func ReadChunk(filename string) { f, err := os.Open(filename) if err != nil {  panic(err) } defer f.Close() buf := make([]byte, 4*1024) r := bufio.NewReader(f) for {  _, err = r.Read(buf)  if err != nil {   if err == io.EOF {    break   }   panic(err)  } }}func BenchmarkReadChunk4KB(b *testing.B) { for i := 0; i 

汇总结果

BenchmarkOsReadFile4KB-8           92877             12491 ns/opBenchmarkOsReadFile4MB-8            1620            744460 ns/opBenchmarkOsReadFile4GB-8               1        7518057733 ns/opsignal: killedBenchmarkReadLines4KB-8            90846             13184 ns/opBenchmarkReadLines4MB-8              493           2338170 ns/opBenchmarkReadLines4GB-8                1        3072629047 ns/opBenchmarkReadLines16GB-8               1        12472749187 ns/opBenchmarkReadChunk4KB-8            99848             12262 ns/opBenchmarkReadChunk4MB-8              913           1233216 ns/opBenchmarkReadChunk4GB-8                1        2095515009 ns/opBenchmarkReadChunk16GB-8               1        8547054349 ns/op

登录后复制

在本文的测试条件下(每行数据 1KB),对于小对象 4KB 的读取,三种方式差距并不大;在 MB 级别的读取中,直接加载最快,但块读取也慢不了多少;上了 GB 后,块读取方式会最快。

且有一点可以注意到的是,在整个文件加载的方式中,对于 16 GB 的文件数据(测试机器运行内存为 8GB),会内存耗尽出错,没法执行。

总结

不管是什么大小的文件,均不推荐整个文件加载的方式,因为它在小文件时的速度优势并没有那么大,相较于安全隐患,不值得选择它。

块读取是优先选择,尤其对于一些没有换行符的文件,例如音视频等。通过设定合适的块读取大小,能让速度和内存得到很好的平衡。且在读取过程中,往往伴随着处理内容的逻辑。每块内容可以赋给一个工作 goroutine 来处理,能更好地并发。

 End 

往期精彩文章推荐:

一篇文章教会你Go语言基础之反射

Go语言基础之结构体(冬日篇)

一篇文章带你了解Go语言基础之map

怎么选择 Go 文件读取方案

欢迎大家点赞,留言,转发,转载,感谢大家的相伴与支持

想加入Go学习群请在后台回复【入群

万水千山总是情,点个【在看】行不行

以上就是怎么选择 Go 文件读取方案的详细内容,更多请关注【创想鸟】其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至253000106@qq.com举报,一经查实,本站将立刻删除。

发布者:PHP中文网,转转请注明出处:https://www.chuangxiangniao.com/p/2487343.html

(0)
上一篇 2025年3月4日 22:26:08
下一篇 2025年2月20日 04:47:45

AD推荐 黄金广告位招租... 更多推荐

相关推荐

  • slog:Go官方的结构化日志包开发的怎么样了?该如何使用?

    熟悉 Go 的同学都知道 Go 语言标准库 log 有许多痛点,比如没有日志分级、没有结构化(没有 JSON 格式)、扩展性差等,为了解决这些问题 Go 官方推出了结构化日志包 slog,目前这个库正在开发阶段,已经进入了实验库:golan…

    2025年3月4日
    200
  • Go语言Websocket开发实践:如何处理连接中断

    Go语言Websocket开发实践:如何处理连接中断 引言:Websocket是一种在Web浏览器和服务器之间进行双向通信的协议,通过这种协议可以实现实时交互和数据传输。在Go语言中,可以方便地使用标准库中提供的github.com/gor…

    2025年3月4日
    200
  • Go语言编程:分号的必要性探讨

    分号在很多编程语言中被用作语句的结束符号,然而在Go语言中却有一些特殊的规则。本文将探讨Go语言中分号的必要性,并通过具体的代码示例来展示分号在不同情况下的作用。 在Go语言中,分号不是必需的。根据Go语言的规范,编译器会在每行结束时自动插…

    2025年3月4日
    200
  • 学习Go语言必备的一个包

    在学习Go语言过程中,有一个非常重要的包是不可或缺的,那就是fmt包。fmt包是Go语言中用来进行格式化输入输出的标准包,它提供了各种格式化输出函数,帮助程序员进行数据的展示和交互。在本文中,我们将介绍fmt包的常见用法,并通过具体的代码示…

    2025年3月4日
    200
  • PHP与Go语言的优劣对比分析

    PHP与Go语言是两种常用的编程语言,它们各有优劣。PHP是一种脚本语言,特别适合用于Web开发,而Go语言则是一种静态类型的编译型语言,被广泛应用于云平台和大规模分布式系统。本文将对PHP与Go语言进行一些优劣对比分析,并提供一些具体的代…

    2025年3月4日
    200
  • Go语言常用的编程语言有哪些?

    标题:Go语言常用的编程语言有哪些? Go语言是一种由Google开发的开源编程语言,它具有简洁、高效、易用等特点,在云计算、分布式系统、网络编程等领域有着广泛的应用。与其他编程语言相比,Go语言有着自己独特的特性和优势,在实际项目中常常搭…

    2025年3月4日
    200
  • Go语言中常用的函数有哪些?

    Go语言中常用的函数有哪些?————探究Go语言中常用的函数及其用法 作为一门流行的静态类型编程语言,Go语言广泛应用于各种领域,如服务器端开发、网络编程、云计算等。在Go语言中,函数作为基本的代码组织单元,是程序运行的重要组成部分。本文将…

    2025年3月4日
    200
  • Go语言库大全:让您轻松调用功能丰富的第三方库

    go语言拥有大量的第三方库,为开发人员提供即用解决方案。本文介绍了以下热门库和其实战案例:网络:net/http:用于构建和处理http服务和客户端。数据库:github.com/go-sql-driver/mysql:提供对mysql数据…

    2025年3月4日
    200
  • 深入理解Golang流程控制语句

    go 语言提供了丰富的流程控制语句,用于控制程序流程流向,包括:条件语句(if、switch);循环语句(for、while);实战案例:计算阶乘使用 if 和 for 语句;其他流程控制语句(break、continue、goto、def…

    2025年3月4日
    200
  • go命令生成功能详解

    go 提供了 “go generate” 命令,它允许根据自定义模板生成代码。该命令接收可选的正则表达式参数 “-run” 和要应用模板的文件列表。模板使用标记 {{.fieldname}} …

    2025年3月4日
    200

发表回复

登录后才能评论