【问题标题】:Python: calculating checksum of a file for a weird protocolPython:为奇怪的协议计算文件的校验和
【发布时间】:2014-09-09 11:03:57
【问题描述】:

我在为一个奇怪的协议计算文件校验和之类的东西时遇到了麻烦,我正在尝试移植到 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

但是计算仍然很慢。我想要一个快速的解决方案,但我不知道该怎么做。

【问题讨论】:

  • 这个一个校验和,但它不是一个CRC,所以在你的代码中使用这个术语不是一个好主意。
  • 文件是否太大而无法放入内存?为什么要使用 mmap 而不是 array 模块或更好的 numpy(如果可用)?
  • 你应该在 read-binary 'rb' 中打开文件。
  • 你不能为 C 代码编写一个 Python 绑定吗? stackoverflow.com/questions/10202306/…
  • 输入文件很大 (16 MB)。这就是使用 mmap 的原因。另外,我不能使用原始的 C 代码,因为它是在 windows machin 中编译的,并且此代码适用于 linux 机器。

标签: python


【解决方案1】:

一种方法是使用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

在我的笔记本电脑上花费不到一秒钟...不确定您希望它运行多快...

【讨论】:

  • 这个版本确实快得多,但我认为(也许我错了)数字是以小端序读取的,所以最终结果是错误的。
  • 完美!一个快速准确的解决方案,正是我所需要的。非常感谢。
【解决方案2】:

我认为你有问题,但不是你描述的那些。

从任意长数字传递到 4 字节整数就像 n4b = n &amp; 0x0FFFFFFFF 一样简单

我不明白为什么计算为 Windows 机器编写的 C 源代码不能在 Unix 机器上编译。

话虽如此,6293623225328814 &amp; 0xFFFFFFFF 以六进制形式给出 14880698060x58b224ae,这不是您的预期结果。我想你有和字节序问题。您应该首先以小端和大端计算它以知道选择哪个。

# 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

【讨论】:

  • 感谢您的解决方案。
  • 首先,我要澄清我没有源代码(我只有可执行文件)。基本上,我正在做逆向工程。所有计算均以大端方式完成。使用按位运算,数字计算正确,但运算仍然很慢。有什么方法可以加快计算速度?
  • @sentencia :除了用 C 重写整个东西(实际上要快得多)之外,我没有更好的主意。
猜你喜欢
  • 1970-01-01
  • 2017-09-24
  • 2012-05-18
  • 1970-01-01
  • 2013-06-25
  • 2021-04-05
  • 1970-01-01
  • 2010-12-18
相关资源
最近更新 更多