有没有比 ioutil.ReadFile 更快的替代方案?

gyu*_*ula 3 go

我正在尝试编写一个基于 md5 校验和检查文件重复项的程序。不太确定我是否遗漏了一些东西,但是这个读取 XCode 安装程序应用程序(它有大约 8GB)的功能使用了 16GB 的 RAM

func search() {
    unique := make(map[string]string)
    files, err := ioutil.ReadDir(".")
    if err != nil {
        log.Println(err)
    }

    for _, file := range files {
        fileName := file.Name()
        fmt.Println("CHECKING:", fileName)
        fi, err := os.Stat(fileName)
        if err != nil {
            fmt.Println(err)
            continue
        }
        if fi.Mode().IsRegular() {
            data, err := ioutil.ReadFile(fileName)
            if err != nil {
                fmt.Println(err)
                continue
            }
            sum := md5.Sum(data)
            hexDigest := hex.EncodeToString(sum[:])
            if _, ok := unique[hexDigest]; ok == false {
                unique[hexDigest] = fileName
            } else {
                fmt.Println("DUPLICATE:", fileName)
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

根据我的调试,问题在于文件读取是否有更好的方法来做到这一点?谢谢

Kir*_*ril 6

Golang 文档中有一个示例,涵盖了您的情况。

package main

import (
    "crypto/md5"
    "fmt"
    "io"
    "log"
    "os"
)

func main() {
    f, err := os.Open("file.txt")
    if err != nil {
        log.Fatal(err)
    }
    defer f.Close()

    h := md5.New()
    if _, err := io.Copy(h, f); err != nil {
        log.Fatal(err)
    }

    fmt.Printf("%x", h.Sum(nil))
}
Run Code Online (Sandbox Code Playgroud)

对于您的情况,只需确保关闭循环中的文件而不是推迟它们。或者将逻辑放入函数中。


two*_*two 5

听起来 16GB RAM 是你的问题,而不是速度本身。

不要使用 ReadFile 将整个文件读入变量;io.Copy 从 Open 为您提供的 Reader 提供给 hash/md5 提供的 Writer(md5.New返回一个hash.Hash,其中嵌入了一个 io.Writer )。一次只复制一点点,而不是将所有文件拉入 RAM。

这是一个在 Go 中很多地方都很有用的技巧;text/templatecompress/gzip、等包net/http按照读者和作者的方式工作。有了它们,您通常不需要创建巨大的[]bytes 或strings;您可以将 I/O 接口相互连接起来,让它们为您传递内容片段。在垃圾收集语言中,节省内存往往也会节省 CPU 工作。