Python: 计算奇怪协议文件的校验和

0 投票
2 回答
770 浏览
提问于 2025-04-18 13:49

我在计算一个文件的校验和时遇到了麻烦,这个校验和是我正在尝试移植到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()

但我发现了两个问题。

  1. 首先,输出的数字太大了。我需要一个4字节的数字。 用之前的代码,典型测试文件的输出是0x165c0458b224ae,但它应该是0xcaac5458(一个4字节的无符号整数)。
  2. 我的方法运行得很慢。原来的代码(用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

在我的笔记本上,这个过程不到一秒钟……我不太确定你希望它快到什么程度……

撰写回答