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


Posted in Golang onMay 08, 2021

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

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

这里直接给出实现源码。

package main
import (
 "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("%x\n", 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("%x\n", 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 file
md5 [-pqrtx] [-s string] [file ...]

测试文件是公司项目的日志文件

banjakukutekiiMac:shell panshiqu$ ls -an | grep by
-rw-r--r--   1 501  20   7285957 11 17 16:14 by.out
banjakukutekiiMac:shell panshiqu$ cp by.out by2.out
banjakukutekiiMac:shell panshiqu$ cat by.out >> by2.out
banjakukutekiiMac: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.027s
user 0m0.017s
sys 0m0.012s
banjakukutekiiMac:shell panshiqu$ time ./gomd5 -cnt=1 -which=true -path="by2.out"
real 0m0.048s
user 0m0.033s
sys 0m0.018s
banjakukutekiiMac:shell panshiqu$ time ./gomd5 -cnt=1 -which=false -path="by.out"
real 0m0.018s
user 0m0.012s
sys 0m0.004s
banjakukutekiiMac:shell panshiqu$ time ./gomd5 -cnt=1 -which=false -path="by2.out"
real 0m0.031s
user 0m0.024s
sys 0m0.005s
banjakukutekiiMac:shell panshiqu$ time md5 by.out
MD5 (by.out) = 9d79e19a00cef1ae1bb6518ca4adf9de
real 0m0.023s
user 0m0.019s
sys 0m0.006s
banjakukutekiiMac:shell panshiqu$ time md5 by2.out
MD5 (by2.out) = 0a029a460a20e8dcb00d032d6fab74c6
real 0m0.042s
user 0m0.037s
sys 0m0.009s

总结:

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

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

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

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

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

const hextable = "0123456789abcdef" 
//作者: pengpengzhou
func FastMD5(str string) string {
	src := md5.Sum([]byte(str))
	var dst = make([]byte, 32)
	j := 0
	for _, v := range src {
		dst[j] = hextable[v>>4]
		dst[j+1] = hextable[v&0x0f]
		j += 2
	}
	return string(dst)
}

Go Test Benchmark测试结果:

goos: linux
goarch: amd64
pkg: example
BenchmarkFastMD5-4       5564898               205 ns/op
BenchmarkV1-4            3461698               379 ns/op
BenchmarkV2-4            2277235               516 ns/op
BenchmarkV3-4            2158122               527 ns/op
PASS
ok      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 := 0
	for _, 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 相关文章推荐
go:垃圾回收GC触发条件详解
Apr 24 Golang
goland 清除所有的默认设置操作
Apr 28 Golang
golang 实现两个结构体复制字段
Apr 28 Golang
浅谈Golang 嵌套 interface 的赋值问题
Apr 29 Golang
对Golang中的FORM相关字段理解
May 02 Golang
解决golang 关于全局变量的坑
May 06 Golang
golang 实现时间戳和时间的转化
May 07 Golang
基于Golang 高并发问题的解决方案
May 08 Golang
简单聊聊Golang中defer预计算参数
Mar 25 Golang
GoFrame基于性能测试得知grpool使用场景
Jun 21 Golang
Go微服务项目配置文件的定义和读取示例详解
Jun 21 Golang
GoLang中生成UUID唯一标识的实现
May 08 #Golang
聊聊golang中多个defer的执行顺序
May 08 #Golang
Golang全局变量加锁的问题解决
golang 实现并发求和
May 08 #Golang
golang中的并发和并行
May 08 #Golang
关于golang高并发的实现与注意事项说明
May 08 #Golang
基于Golang 高并发问题的解决方案
May 08 #Golang
You might like
PHP常用代码大全(新手入门必备)
2010/06/29 PHP
解析php利用正则表达式解决采集内容排版的问题
2013/06/20 PHP
浅析使用Turck-mmcache编译来加速、优化PHP代码
2013/06/20 PHP
php可变长参数处理函数详解
2017/02/22 PHP
PHP共享内存使用与信号控制实例分析
2018/05/09 PHP
PHP中-&gt;和=&gt;的含义及使用示例解析
2020/08/06 PHP
javascript 用记忆函数快速计算递归函数
2010/03/15 Javascript
JavaScript基本编码模式小结
2012/05/23 Javascript
jquery formValidator插件ajax验证 内容不做任何修改再离开提示错误的bug解决方法
2013/01/04 Javascript
判断浏览器的内核及版本号方法汇总
2015/01/05 Javascript
javascript 实现map集合
2015/04/03 Javascript
深入浅析JavaScript系列(13):This? Yes,this!
2016/01/05 Javascript
简单实现js间歇或无缝滚动效果
2016/06/29 Javascript
jQuery检查元素存在性(推荐)
2016/09/17 Javascript
JS百度地图搜索悬浮窗功能
2017/01/12 Javascript
利用JavaScript在网页实现八数码启发式A*算法动画效果
2017/04/16 Javascript
理解 javascript 中的函数表达式与函数声明
2017/07/07 Javascript
JavaScript实现的原生态兼容IE6可调可控滚动文字功能详解
2017/09/19 Javascript
nodejs多版本管理总结
2018/04/03 NodeJs
微信小程序实现banner图轮播效果
2020/06/28 Javascript
Vue起步(无cli)的啊教程详解
2019/04/11 Javascript
node.js使用yargs处理命令行参数操作示例
2020/02/11 Javascript
js实现拖拽元素选择和删除
2020/08/25 Javascript
用vue设计一个日历表
2020/12/03 Vue.js
Python实现的多项式拟合功能示例【基于matplotlib】
2018/05/15 Python
Python操作Excel把数据分给sheet
2020/05/20 Python
python 解决mysql where in 对列表(list,,array)问题
2020/06/06 Python
python有几个版本
2020/06/17 Python
经验丰富程序员才知道的8种高级Python技巧
2020/07/27 Python
Html5 滚动穿透的方法
2019/05/13 HTML / CSS
一些Solaris面试题
2015/12/22 面试题
应用化学专业本科生求职信
2013/09/29 职场文书
2014年社区学雷锋活动总结
2014/03/09 职场文书
2014年禁毒工作总结
2014/11/24 职场文书
Vue CLI中模式与环境变量的深入详解
2021/05/30 Vue.js
MutationObserver在页面水印实现起到的作用详解
2022/07/07 Javascript