File io go中的文件读取和校验和。方法之间的差异

File io go中的文件读取和校验和。方法之间的差异,file-io,go,sha512,File Io,Go,Sha512,最近我开始在go中为文件创建校验和。我的代码处理大小文件。我尝试了两种方法,第一种是使用ioutil.ReadFile(“文件名”),第二种是使用os.Open(“文件名”) 示例: 第一个函数使用的是io/ioutil,适用于小文件。当我试图复制一个大文件时,我的ram会受到冲击,对于1.5GB的iso,它使用3GB的ram func byteCopy(fileToCopy string) { file, err := ioutil.ReadFile(fileToCopy) //1.5

最近我开始在go中为文件创建校验和。我的代码处理大小文件。我尝试了两种方法,第一种是使用
ioutil.ReadFile(“文件名”)
,第二种是使用
os.Open(“文件名”)

示例:

第一个函数使用的是
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和示例。 拥有可用的代码是很好的,但要理解为什么它可以正常工作就远远不止这些了


提前谢谢

ioutil.ReadFile
工作正常。这是你的错,滥用系统资源,使用该功能的事情,你知道是巨大的


ioutil.ReadFile
是一个方便的文件助手,您可以事先确定这些文件会很小。像配置文件一样,大多数源代码文件等(实际上它正在优化对象。

ioutil.ReadFile
工作正常。将该功能用于您知道非常庞大的对象,滥用系统资源是您的错


ioutil.ReadFile
是一个方便的文件助手,您可以事先确定这些文件会很小。像配置文件、大多数源代码文件等(实际上它正在优化这些文件)。

下面的代码并没有做您认为它会做的事情

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位,这意味着我们正在讨论t不小于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示例中,这两个资源在互不依赖的情况下同步使用。

下面的代码并不像您想象的那样

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位,这意味着我们正在讨论t不小于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示例中,这两个资源在互不依赖时同步使用