无尘阁日记

无尘阁日记

同一个md5在同一个docker容器里执行的,结果不一样?为什么?
2025-09-29

结论先说:MD5 不会“随机变”,同一字符串在同一环境下哈希值一定一样。
你两次得到不同结果,几乎可以确定是两次输入的字符串并不完全相同——多半混入了不可见字符不同的符号(如全角/半角、不同破折号、不可见空格、换行/回车等)。

你给的字符串 f71fbbf3-d140-3fff-96ed-e1ab7c44327a+3 的标准 MD5 是:

5ecf5cda35d8eb95d2b2877364f7fff0

(这正是你第二次的结果。)
第一次那个 d2eaf241770f83d54f9e79c5135baa16 说明当时的实际字节序列不等于上面这串字符。

常见“坑”举例(看不见但会改变 MD5):

  • 末尾/开头多了空格、Tab\n\r

  • 从网页复制带上了不间断空格(NBSP,U+00A0)、零宽空格(ZWSP,U+200B);

  • 破折号/连字符不是普通 -(比如 /);

  • 加号不是 ASCII +(用了全角 );

  • 复制时混入了看不见的控制字符或 BOM。

一步查明差异(推荐直接在容器里跑)

php -r '
$s = "f71fbbf3-d140-3fff-96ed-e1ab7c44327a+3";
echo "md5: ", md5($s), PHP_EOL;
echo "len: ", strlen($s), PHP_EOL;             // 字节长度
for ($i=0; $i<strlen($s); $i++) {
    printf("%02X ", ord($s[$i]));              // 每个字节的十六进制
}
echo PHP_EOL;
'

如果你是从别处复制的那一段,把那 原样粘进去 再跑一遍(或读入文件再打印字节),两次的 len 或字节序列必然不同。

如何“净化”输入再算 MD5

$s = $raw; // 原字符串

// 1) 去掉常见不可见空白(普通空格/Tab/不间断空格/零宽空格等)
$s = preg_replace('/[\h\p{Z}\x{200B}\x{FEFF}]+/u', ' ', $s);

// 2) 统一换行
$s = str_replace(["\r\n", "\r"], "\n", $s);

// 3) 去首尾空白
$s = trim($s);

// 4) 如有可能的全角字符,按需要做替换(示例把全角+/-替成半角)
$s = strtr($s, ['+' => '+', '-' => '-', '—' => '-', '–' => '-']);

echo md5($s);

排错小贴士

  • 别在命令里直接手敲很长的字符串做多次测试,容易误差;把待测内容放进同一段代码同一文件里重复调用。

  • bin2hex($s) 或上面的 ord() 循环把每个字节打印出来,最直观。

  • 从网页复制的内容最容易混入 NBSP/ZWSP;从 Windows 复制的文本容易带 \r\n

  • CLI 里 echo md5(...); 默认不换行,可用 printf("%s\n", md5(...)); 让输出更清晰。

只要对比出字节级差异,就能解释为什么第一次和第二次的 MD5 不一样;把输入“净化一致”后,结果就会稳定为 5ecf5cda35d8eb95d2b2877364f7fff0