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

近期有一个需求:获取多个文件 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)
	}
}

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

(0)

相关推荐

  • 基于Golang 高并发问题的解决方案

    Golang 高并发问题的解决 Golang在高并发问题上,由于协程的使用,相对于其他编程语言,已经有了很大的优势,即相同的配置上,Golang可以以更低的代价处理更多的线程,同样的线程数,占用更低的资源!及时这样,只是解决了一部分问题而已,因为在每个协程里,处理逻辑还是会有问题. 高并发时,还是要考虑服务器所能承受的最大压力,数据库读取时的io问题,连接数问题,带宽问题等等 研究了一下并发解决方案,在此记录一下 参考文章:Handling 1 Million Requests per Minu

  • golang 实现并发求和

    使用golang并发求和,作为对golang并发的一个练习. 为了验证结果的正确性,要给出最传统的版本: func sum1(data []int) int { s := 0 l := len(data) for i := 0; i < l; i++ { s += data[i] } return s } 第二种方法 使用N个goroutine, 然后将N个分段的和写入N个channel中: func sum2(data []int) int { s := 0 l := len(data) co

  • GoLang中生成UUID唯一标识的实现

    1.什么是UUID UUID简介 通用唯一识别码(英语:Universally Unique Identifier,简称UUID)是一种软件建构的标准,亦为自由软件基金会组织在分散式计算环境领域的一部份. UUID的目的,是让分散式系统中的所有元素,都能有唯一的辨识信息,而不需要通过中央控制端来做辨识信息的指定. 如此一来,每个人都可以创建不与其它人冲突的UUID. 在这样的情况下,就不需考虑数据库创建时的名称重复问题.目前最广泛应用的UUID,是微软公司的全局唯一标识符(GUID),而其他重要

  • Golang全局变量加锁的问题解决

    如果全局变量只读取 那自然是不需要加锁的 如果全局变量多进程读,多进程写,那自然是需要加读写锁的 但是如果全局变量只有一个进程写,其他进程读呢? 如果采用COW的方式,写进程只是通过单次赋值的方式来更新变量,是否就可以不加锁了呢? 就第三种情况而言: 当然我们通过 go build -race 或者 go run -race 就会出现 WARNING: DATA RACE. 但是出现 data race 就证明一定有问题么? 其实核心点在于这个赋值是否是原子的.也就是说是否存在 p1 = p2

  • 聊聊golang中多个defer的执行顺序

    golang 中多个 defer 的执行顺序 引用 Ture Go 中的一个示例: package main import "fmt" func main() { fmt.Println("counting") for i := 0; i < 10; i++ { defer fmt.Println(i) } fmt.Println("done") } 程序执行结果为: counting done 9 8 7 6 5 4 3 2 1 0 从结

  • Golang的md5 hash计算操作

    Golang计算md5值的方法都是接收byte型slice([]byte).而且使用习惯上也觉得略奇怪. 看了好几个例子才看懂. 感觉Golang标准库在设计这些模块的时候,都会考虑使用带New关键字工厂生成一个该类型的结构体对象.然后再使用改对象的方法进行操作. md5包就是这样,来看例子: s := "api_key" + ApiKey + "param" + Param + "time" + time + "version&quo

  • 关于golang高并发的实现与注意事项说明

    一.并发的意义 并发的意义就是让 一个程序同时做多件事情,其目的只是为了能让程序同时做另一件事情而已,而不是为了让程序运行的更快(如果是多核处理器,而且任务可以分成相互独立的部分,那么并发确实可以让事情解决的更快). golang从语言级别上对并发提供了支持,而且在启动并发的方式上直接添加了语言级的关键字,不必非要按照固定的格式来定义线程函数,也不必因为启动线程的时候只能给线程函数传递一个参数而烦恼. 二.并发的启动 go的并发启动非常简单,几乎没有什么额外的准备工作,要并发的函数和一般的函数没

  • golang中的并发和并行

    golang中默认使用一个CPU,这时程序无法并发,只能是并发.因为始终只有一个CPU在运行. package main import ( "fmt" "runtime" ) //并发和并行 var quit chan int = make(chan int) func loop() { for i := 0; i < 100; i++ { //为了观察,跑多些 fmt.Printf("%d ", i) } quit <- 0 } f

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

    近期有一个需求:获取多个文件 md5 校验和判断是否存在重复文件,因为文件数量较多,有的文件还比较大,需要处理的文件还没有到位,我就考虑了一下效率的问题. 目前我已知的 Golang 中获取 md5 校验和的方法有两个 这里直接给出实现源码. package main import ( "crypto/md5" "flag" "fmt" "io" "io/ioutil" "os" ) v

  • C#计算文件MD5校验的方法

    本文实例讲述了C#计算文件MD5校验的方法.分享给大家供大家参考.具体分析如下: C#计算文件的MD5校验,从互联网上下载一个文件,可以使用下面的代码对文件进行MD5校验,看看生成的MD5编码是否和网站提供的一致,如果不一致则表示文件被修改过了,要慎重. protected string GetMD5HashFromFile(string fileName) { FileStream file = new FileStream(fileName,FileMode.Open); MD5 md5 =

  • C#获取文件MD5值的实现示例

    前言 MD5是一种常见的加密方式,相对比较稳定,同时也是校验文件的一种方式,本文给大家介绍了利用C#获取文件MD5值的方法,直接使用即可,可用于对比文件是否相同.下面话不多说,来看示例代码吧 示例代码如下: /// <summary> /// 获取文件MD5值 /// </summary> /// <param name="fileName">文件绝对路径</param> /// <returns>MD5值</retur

  • 详解JAVA中获取文件MD5值的四种方法

    JAVA中获取文件MD5值的四种方法其实都很类似,因为核心都是通过JAVA自带的MessageDigest类来实现.获取文件MD5值主要分为三个步骤,第一步获取文件的byte信息,第二步通过MessageDigest类进行MD5加密,第三步转换成16进制的MD5码值.几种方法的不同点主要在第一步和第三步上.具体可以看下面的例子: 方法一. private final static String[] strHex = { "0", "1", "2"

  • Python实现通过文件路径获取文件hash值的方法

    本文实例讲述了Python实现通过文件路径获取文件hash值的方法.分享给大家供大家参考,具体如下: import hashlib import os,sys def CalcSha1(filepath): with open(filepath,'rb') as f: sha1obj = hashlib.sha1() sha1obj.update(f.read()) hash = sha1obj.hexdigest() print(hash) return hash def CalcMD5(fi

  • Python实现检测文件MD5值的方法示例

    本文实例讲述了Python实现检测文件MD5值的方法.分享给大家供大家参考,具体如下: 前面介绍过Python计算文件md5值的方法,这里分析一下Python检测文件MD5值的另一种实现方法. 概述: MD5(单向散列算法)的全称是Message-Digest Algorithm 5(信息-摘要算法),经MD2.MD3和MD4发展而来.MD5算法的使用不需要支付任何版权费用. 实现代码: #python 检测文件MD5值 #python version 2.6 import hashlib im

  • Python简单计算文件MD5值的方法示例

    本文实例讲述了Python简单计算文件MD5值的方法.分享给大家供大家参考,具体如下: 一 代码 import sys import hashlib import os.path filename = sys.argv[1] if os.path.isfile(filename): fp=open(filename,'rb') contents=fp.read() fp.close() print(hashlib.md5(contents).hexdigest()) else: print('f

  • GoLang读取文件的10种方法实例

    目录 一. 整个文件读入内存 1.直接指定文化名读取 1.1使用os.ReadFile函数读取文件 2.先创建句柄再读取 2.1使用os.OpenFile函数只读形式获取句柄 2.2代码讲解 二.每次只读取一行 1.使用bufio.Reader结构体的ReadBytes方法读取字节数 2.使用bufio.Reader结构体的ReadString方法读取字符串 3.代码讲解 3.1bufio.Reader结构体 三.每次只读取固定字节数 1.使用os库 2.使用 syscall库 总结 一. 整个

  • VB.NET获取文件默认图标的方法

    本文实例讲述了VB.NET获取文件默认图标的方法.分享给大家供大家参考.具体如下: 该段代码帮助你获取计算机上的任何文件的默认图标,使用Shell32.dll. Private Structure SHFILEINFO Public hIcon As IntPtr Public iIcon As Integer Public dwAttributes As Integer <MarshalAs(UnmanagedType.ByValTStr, SizeConst:=260)> _ Public

  • Python获取文件ssdeep值的方法

    本文实例讲述了Python获取文件ssdeep值的方法,分享给大家供大家参考.具体方法如下: 首先,得到ssdeep值,需要先import ssdeep 在ubuntu上安装pyssdeep时 一直出错  后来发现apt-cache search "ssdeep"时把几个全apt-get install 上,但问题依旧. 后来下载到pyssdeep的源文件 ,tar zxvf pyssdeep.tar.zip 然后 apt-get install python-dev 然后 pytho

随机推荐