文件阅读和校验和。方法之间的区别

时间:2013-08-25 16:05:01

标签: file-io go sha512

最近,我正在为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/sha512io/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 个答案:

答案 0 :(得分:3)

以下代码不符合您的预期。

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示例中,两个资源在不相互依赖时会同步使用。

答案 1 :(得分:1)

ioutil.ReadFile正常工作。滥用系统资源是错误的,因为你知道这些功能非常庞大。

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

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