如何让Python实现的Jenkins one_at_a_time哈希与JS版本输出一致
问题根因
两段代码结果不一致来自三个核心差异:
- 字符编码处理不一致:JS的
charCodeAt返回字符的UTF-16编码单元值,你原Python代码中ord(char.encode("utf-8"))逻辑错误,对于多字节的UTF-8字符只会取第一个字节的编码值,和JS的输入处理逻辑不匹配。 - 移位操作行为不一致:JS的
>>是有符号右移,会保留符号位;你原Python代码中的右移是对32位无符号整数做算术右移,和JS的移位结果完全不同。 - 输出格式不一致:Python的
hex()函数会返回带0x前缀的字符串,且不会自动补前导零到8位,和JStoString(16)的输出格式不匹配。
修正后的Python代码(完全对齐JS输出)
import ctypes def calculateChecksum(keyString: str): # Credits(modified code): Bob Jenkins # 对齐魔改JS版本的Jenkins one-at-a-time哈希实现 hash = 0 for char in keyString: # 对齐JS charCodeAt的UTF-16码点取值(BMP字符下等价于Unicode码点) hash += ord(char) hash &= 0xFFFFFFFF hash += hash << 10 hash &= 0xFFFFFFFF # 模拟JS的32位有符号右移6位 signed_hash = ctypes.c_int32(hash).value hash ^= signed_hash >> 6 hash &= 0xFFFFFFFF hash += hash << 3 hash &= 0xFFFFFFFF # 模拟JS的32位有符号右移11位 signed_hash = ctypes.c_int32(hash).value hash ^= signed_hash >> 11 hash &= 0xFFFFFFFF hash += hash << 15 hash &= 0xFFFFFFFF # 对齐JS toString(16)输出格式:去除0x前缀,补前导零到8位 return hex(hash)[2:].zfill(8)
JS代码和维基示例不匹配的原因
你使用的JS代码是魔改后的版本,并非Jenkins one-at-a-time哈希的标准实现,改动了两个核心逻辑:
- 输入处理从标准的字节序列改为UTF-16编码单元
- 移位操作使用有符号右移,而非标准实现的无符号右移
因此JS代码的结果和维基百科的标准实现示例不一致,你最初编写的Python代码是对齐标准实现的,所以会和维基示例匹配。
内容的提问来源于stack exchange,提问作者Khi-Lee Klock
相关产品推荐
相关产品推荐

