Python: 计算奇怪协议文件的校验和
我在计算一个文件的校验和时遇到了麻烦,这个校验和是我正在尝试移植到Python的一个奇怪协议的一部分。
校验和是一个4字节的无符号整数,它是将文件中所有4字节的无符号整数相加的结果。例如,假设有一个这样的文件(注意,真实的文件大约有16MB):
ff fe fd fc fb fa f9 f8 f7 f6 f5 f4 f3 f2 f1 f0
ef ee ed ec eb ea e9 e8 e7 e6
在我的实现中(见下面),计算过程是这样的:
0xfffefdfc + 0xfbfaf9f8 + 0xf7f6f5f4 + 0xf3f2f1f0 + 0xefeeedec + 0xebeae9e8 + 0xe7e60000 = 0x6aba3b7ac
但是,结果应该是0xaba3b7ac
我尝试做了这样的事情:
import mmap
import struct
# Prepare file
file = open("file.bin", 'rb')
map = mmap.mmap(file.fileno(), 0, access=mmap.ACCESS_READ)
# Calculate checksum
checksum = 0
while (map.tell() < map.size()):
checksum += struct.unpack('>I', map.read(4))[0]
print "checksum: ", checksum
# Close file.
map.close()
file.close()
但我发现了两个问题。
- 首先,输出的数字太大了。我需要一个4字节的数字。
用之前的代码,典型测试文件的输出是
0x165c0458b224ae
,但它应该是0xcaac5458
(一个4字节的无符号整数)。 - 我的方法运行得很慢。原来的代码(用C语言写的)计算速度快得多。
我真的被这个问题困住了,所以任何帮助都将非常感激。
提前谢谢你们,抱歉我的英语不好。
更新:
第一个问题已经被Serge Ballesta解决了。解决方案是在打印校验和之前添加以下一行:
checksum &= 0xffffffff
但是计算仍然很慢。我希望能有一个快速的解决方案,但我不知道该怎么做。
2 个回答
1
我觉得你遇到了一些问题,但可能不是你描述的那些。
把一个任意长的数字转换成一个4字节的整数其实很简单,就像这样:n4b = n & 0x0FFFFFFFF
我不明白为什么为Windows机器写的C源代码不能在Unix机器上编译。
说到这里,6293623225328814 & 0xFFFFFFFF
的结果是1488069806
,或者用十六进制表示是0x58b224ae
,这并不是你预期的结果。我猜你可能遇到了字节序的问题。你应该先计算一下小端和大端格式,看看该选择哪个。
# Calculate checksum
checksum_be = 0
checksum_le = 0
while (map.tell() < map.size()):
checksum_be += struct.unpack('>I', map.read(4))[0]
checksum_le += struct.unpack('<I', map.read(4))[0]
checksum_be &= 0x0FFFFFFFF
checksum_le &= 0x0FFFFFFFF
print "checksums: ", checksum_be, checksum_le
2
一种方法是使用 array
模块,把四个字节的数据全部加载到内存中,然后把它们加起来,最后再强制转换回4个字节的格式(正如Serge已经提到的)。
import os
from array import array
#with open('data.bin', 'wb') as fout:
# fout.write(os.urandom(16800000))
with open('data.bin', 'rb') as fin:
arr = array('L')
arr.fromfile(fin, 16800000 / 4)
arr.byteswap()
checksum = sum(arr) & 0xFFFFFFFF
在我的笔记本上,这个过程不到一秒钟……我不太确定你希望它快到什么程度……