Golang 获取文件md5校验的方法以及效率对比

【字号: 日期:2022-07-30浏览:31作者:雯心

近期有一个需求:获取多个文件 md5 校验和判断是否存在重复文件,因为文件数量较多,有的文件还比较大,需要处理的文件还没有到位,我就考虑了一下效率的问题。

目前我已知的 Golang 中获取 md5 校验和的方法有两个

这里直接给出实现源码。

package mainimport ( 'crypto/md5' 'flag' 'fmt' 'io' 'io/ioutil' 'os')var which = flag.Bool('which', true, '')var path = flag.String('path', '', '')var cnt = flag.Int('cnt', 100, '')func aaa() { f, err := os.Open(*path) if err != nil { fmt.Println('Open', err) return } defer f.Close() body, err := ioutil.ReadAll(f) if err != nil { fmt.Println('ReadAll', err) return } md5.Sum(body) //fmt.Printf('%xn', md5.Sum(body))}func bbb() { f, err := os.Open(*path) if err != nil { fmt.Println('Open', err) return } defer f.Close() md5hash := md5.New() if _, err := io.Copy(md5hash, f); err != nil { fmt.Println('Copy', err) return } md5hash.Sum(nil) //fmt.Printf('%xn', md5hash.Sum(nil))}func main() { flag.Parse() for i := 0; i < *cnt; i++ { if *which { aaa() } else { bbb() } }}还有可供参考的获取 md5 校验和的 Shell 命令

md5 -- calculate a message-digest fingerprint (checksum) for a filemd5 [-pqrtx] [-s string] [file ...]测试文件是公司项目的日志文件

banjakukutekiiMac:shell panshiqu$ ls -an | grep by-rw-r--r-- 1 501 20 7285957 11 17 16:14 by.outbanjakukutekiiMac:shell panshiqu$ cp by.out by2.outbanjakukutekiiMac:shell panshiqu$ cat by.out >> by2.outbanjakukutekiiMac:shell panshiqu$ ls -an | grep by-rw-r--r-- 1 501 20 7285957 11 17 16:14 by.out-rw-r--r-- 1 501 20 14571914 11 17 17:03 by2.out下面效率展示

banjakukutekiiMac:shell panshiqu$ time ./gomd5 -cnt=1 -which=true -path='by.out'real 0m0.027suser 0m0.017ssys 0m0.012sbanjakukutekiiMac:shell panshiqu$ time ./gomd5 -cnt=1 -which=true -path='by2.out'real 0m0.048suser 0m0.033ssys 0m0.018sbanjakukutekiiMac:shell panshiqu$ time ./gomd5 -cnt=1 -which=false -path='by.out'real 0m0.018suser 0m0.012ssys 0m0.004sbanjakukutekiiMac:shell panshiqu$ time ./gomd5 -cnt=1 -which=false -path='by2.out'real 0m0.031suser 0m0.024ssys 0m0.005sbanjakukutekiiMac:shell panshiqu$ time md5 by.outMD5 (by.out) = 9d79e19a00cef1ae1bb6518ca4adf9dereal 0m0.023suser 0m0.019ssys 0m0.006sbanjakukutekiiMac:shell panshiqu$ time md5 by2.outMD5 (by2.out) = 0a029a460a20e8dcb00d032d6fab74c6real 0m0.042suser 0m0.037ssys 0m0.009s总结:

不管什么方法都会随着文件变大时间会变长,上面的例子大约都是2倍

io.Copy 方法效率最高,建议大家这样使用

补充:Go语言:md5计算方法的效率研究

研究了一下Go的md5计算方法,目前来看,效率最高运行最快的写法是调用md5.Sum()函数返回16字节checksum,然后把每个字节的高4位和低4位分别映射成16进制字符存到两个字节里,得到32字节,再转成字符串。

FastMD5较其它算法效率提高了至少46%以上。

const hextable = '0123456789abcdef' //作者: pengpengzhoufunc FastMD5(str string) string {src := md5.Sum([]byte(str))var dst = make([]byte, 32)j := 0for _, v := range src {dst[j] = hextable[v>>4]dst[j+1] = hextable[v&0x0f]j += 2}return string(dst)}Go Test Benchmark测试结果:

goos: linuxgoarch: amd64pkg: exampleBenchmarkFastMD5-4 5564898 205 ns/opBenchmarkV1-4 3461698 379 ns/opBenchmarkV2-4 2277235 516 ns/opBenchmarkV3-4 2158122 527 ns/opPASSok example 6.440s详细代码如下:

package main import ('crypto/md5''encoding/hex''fmt''io') const hextable = '0123456789abcdef' func FastMD5(str string) string {src := md5.Sum([]byte(str))var dst = make([]byte, 32)j := 0for _, v := range src {dst[j] = hextable[v>>4]dst[j+1] = hextable[v&0x0f]j += 2}return string(dst)} func md5V1(str string) string {h := md5.New()h.Write([]byte(str))return hex.EncodeToString(h.Sum(nil))} func md5V2(str string) string {data := []byte(str)has := md5.Sum(data)md5str := fmt.Sprintf('%x', has)return md5str} func md5V3(str string) string {w := md5.New()io.WriteString(w, str)md5str := fmt.Sprintf('%x', w.Sum(nil))return md5str} func main() {str := '中文'fmt.Println(FastMD5(str))fmt.Println(md5V1(str))fmt.Println(md5V2(str))fmt.Println(md5V3(str))}

package main import ('testing') var str = 'golang中文教程' func BenchmarkFastMD5(b *testing.B) {for i := 0; i < b.N; i++ {FastMD5(str)}} func BenchmarkV1(b *testing.B) {for i := 0; i < b.N; i++ {md5V1(str)}} func BenchmarkV2(b *testing.B) {for i := 0; i < b.N; i++ {md5V2(str)}} func BenchmarkV3(b *testing.B) {for i := 0; i < b.N; i++ {md5V3(str)}}

以上为个人经验,希望能给大家一个参考,也希望大家多多支持乐呵呵网。如有错误或未考虑完全的地方,望不吝赐教。

标签: Golang
相关文章: