为了账号安全,请及时绑定邮箱和手机立即绑定

go中的文件读取和校验和。方法之间的区别

go中的文件读取和校验和。方法之间的区别

Go
绝地无双 2021-06-19 10:02:46
最近我开始为 go 中的文件创建校验和。我的代码正在处理大小文件。我尝试了两种方法,第一种使用ioutil.ReadFile("filename"),第二种使用os.Open("filename").例子:第一个功能是处理io/ioutil小文件。当我尝试复制一个大文件时,我的 ram 会出现爆炸,对于 1.5GB 的 iso,它使用 3GB 的 ram。func byteCopy(fileToCopy string) {    file, err := ioutil.ReadFile(fileToCopy) //1.5GB file    omg(err)                                 //error handling function    ioutil.WriteFile("2.iso", file, 0777)    os.Remove("2.iso")}更糟糕的是,当我想使用crypto/sha512和创建校验和时io/ioutil。它永远不会完成并中止,因为它耗尽了内存。func ioutilHash() {    file, _ := ioutil.ReadFile(iso)    h := sha512.New()    fmt.Printf("%x", h.Sum(file))}使用下面的功能时,一切正常。func ioHash() {    f, err := os.Open(iso) //iso is a big ~ 1.5tb file    omg(err)               //error handling function    defer f.Close()    h := sha512.New()    io.Copy(h, f)    fmt.Printf("%x", h.Sum(nil))}我的问题:为什么该ioutil.ReadFile()功能无法正常工作?1.5GB 的文件不应该填满我的 16GB 内存。我现在不知道去哪里看。有人可以解释这些方法之间的差异吗?我不明白阅读 go-doc 和示例。拥有可用的代码很好,但理解为什么它的工作远高于此。
查看完整描述

2 回答

?
缥缈止盈

TA贡献2041条经验 获得超4个赞

下面的代码没有做你认为的那样。


func ioutilHash() {

    file, _ := ioutil.ReadFile(iso)

    h := sha512.New()

    fmt.Printf("%x", h.Sum(file))

}

这首先读取您的 1.5GB iso。正如 jnml 所指出的,它不断地制造越来越大的缓冲区来填充它。最后,总缓冲区大小不小于 1.5GB 且不大于 1.875GB(按当前实现)。


但是,在那之后,您再创建另一个缓冲区!h.Sum(file)不散列文件。它将当前哈希附加到文件中!这可能会也可能不会导致另一个分配。


真正的问题是您正在获取该文件,现在附加了散列,并用 %x 打印它。fmt 实际上是使用 jnml 指出的 ioutil.ReadAll 所使用的相同类型的方法进行预计算的。因此它不断分配越来越大的缓冲区来存储文件的十六进制。由于每个字母是 4 位,这意味着我们谈论的是不小于 3GB 的缓冲区,不大于 3.75GB。


这意味着您的活动缓冲区可能有 5.625GB 大。将其与 GC 不完美且未删除所有中间缓冲区相结合,它很容易填满您的空间。


编写该代码的正确方法是。


func ioutilHash() {

    file, _ := ioutil.ReadFile(iso)

    h := sha512.New()

    h.Write(file)

    fmt.Printf("%x", h.Sum(nil))

}

这几乎没有分配的数量。


底线是 ReadFile 很少是您想要使用的。IO 流(使用读取器和写入器)总是最好的选择。当你使用 io.Copy 时,你不仅分配的更少,而且你还同时散列和读取磁盘。在您的 ReadFile 示例中,这两个资源在彼此不依赖时同步使用。


查看完整回答
反对 回复 2021-06-28
?
Cats萌萌

TA贡献1805条经验 获得超9个赞

ioutil.ReadFile工作正常。通过使用该功能来滥用系统资源来处理您知道很大的事情是您的错。

ioutil.ReadFile是一个方便的文件帮助程序,您事先很确定它们会很小。像配置文件,大多数源代码文件等(实际上它正在优化文件 <= 1e9 bytes,但这是一个实现细节,而不是 API 合同的一部分。您的 1.5GB 文件强制它使用切片增长,从而分配超过在读取文件的过程中为您的数据提供一个缓冲区。)

即使你使用的其他方法os.File也不好。您绝对应该使用“bufio”包来顺序处理大文件,请参阅bufio.NewReader


查看完整回答
反对 回复 2021-06-28
  • 2 回答
  • 0 关注
  • 230 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信