在Linux下,我们经常使用md5sum命令来查看两个文件是否相同。
md5sum命令会逐位对文件的内容进行校验。是文件的内容,与文件名无关,也就是文件内容相同,其md5值相同。
存在两个文件不同,但md5sum相同的情况。不过这个情况出现的概率还是很低的,所以md5值仍然是常用的方法。
md5值是一个128位的二进制数据,转换成16进制则是32(128/4)位的进制值。
如果对结果的准确性要求极高,可以使用sha系列算法,比如SHA1、SHA256、SHA384、SHA512等。
如前所述,直接对文件名运行md5sum命令,它计算的是文件内容的md5值,那么如何对指定字符串求md5值呢?
在Linux上运行md5sum命令可得到文件内容的md5值:
% cat a.csv
abc
% md5sum a.csv
0bee89b07a248e27c83fc3d5951213c1 a.csv
% cp a.csv b.csv
% md5sum b.csv
0bee89b07a248e27c83fc3d5951213c1 b.csv
如上,两个文件名不同,但文件内容相同,所以md5sum得到的值相同。
如果想对指定字符串计算md5,可以借助管道:
% echo abc | md5sum
0bee89b07a248e27c83fc3d5951213c1 -
% echo abc | md5sum | cut -d ' ' -f 1
0bee89b07a248e27c83fc3d5951213c1
注意,echo命令默认是带换行符的,使用-n参数只计算看到的字符串的md5:
% echo -n abc | md5sum | cut -d ' ' -f 1
900150983cd24fb0d6963f7d28e17f72
这样就可以计算任意给定字符串的md5了,脚本如下:
# 计算目录下所有zip文件名的md5值
#!/bin/bash
function filename2md5sum()
{
sum=$(echo -n $1 | md5sum | cut -d ' ' -f 1)
echo $sum
}
function change_filenames()
{
count=0
for file in *.zip
do
if test -f $file; then
echo $file:
filename2md5sum $file
ret=$?
if [ $ret -eq 0 ]; then
count=$(($count+1))
fi
fi
done
echo $count 个文件名已输出
}
change_filenames
使用hashlib,直接上代码了:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import hashlib
import os
from pathlib import Path
def get_md5_value(src):
md5 = hashlib.md5()
md5.update(src)
md5sum = md5.hexdigest()
return md5sum
def main():
basedir = '.'
for root, ds, fs in os.walk(basedir):
for f in fs:
if f.endswith('.zip'):
# use encode by utf-8 avoid error: Unicode-objects must be encoded before hashing
md5sum = get_md5_value(f.encode('utf-8'))
print('file: {}\n{}'.format(f,md5sum))
if __name__== '__main__':
main()
使用popen得到命令行执行的输出结果,代码如下:
#include <string>
#include <iostream>
using namespace std;
string get_md5sum(const string& filename) {
string md5string;
string cmd = "echo -n '" + filename + "' | md5sum | cut -d ' ' -f 1";
FILE* pipe = popen(cmd.c_str(), "r");
if (pipe != NULL) {
static const unsigned md5size = 32;
unsigned char md5res[md5size + 1];
int readSize = fread((void*)md5res, sizeof(char), md5size, pipe);
pclose(pipe);
if (readSize != md5size) {
std::cout << "Error reading md5" << std::endl;
return md5string;
}
md5res[md5size] = 0;
md5string.assign(&md5res[0], &md5res[md5size]);
}
return md5string;
}
int main(int argc, char* argv[]) {
if (argc != 2) {
cout << "Usage: " << argv[0] << " string4md5sum" << endl;
return 0;
}
string filename = argv[1];
cout << get_md5sum(filename) << endl;
return 0;
}
只要在一种平台下把它原理搞清楚,在其他平台复现只是熟悉程度的问题了。
这只是一个小demo,可以在此基础上进行扩展,实现更具意义的功能了。