Python中的CRC32计算而不使用库

Posted

技术标签:

【中文标题】Python中的CRC32计算而不使用库【英文标题】:CRC32 calculation in Python without using libraries 【发布时间】:2017-05-24 17:02:27 【问题描述】:

我一直试图了解 CRC32 计算但没有取得多大成功,我得到的值似乎与我应该得到的不匹配。

我知道 Python 有能够生成这些校验和的库(即 zlib 和 binascii),但我没有能力使用它们,因为 micropython 上不存在 CRC 功能。

到目前为止,我有以下代码:

import binascii
import zlib
from array import array

poly = 0xEDB88320

table = array('L')
for byte in range(256):
    crc = 0
    for bit in range(8):
        if (byte ^ crc) & 1:
            crc = (crc >> 1) ^ poly
        else:
            crc >>= 1
        byte >>= 1
    table.append(crc)

def crc32(string):
    value = 0xffffffffL

    for ch in string:
        value = table[(ord(ch) ^ value) & 0x000000ffL] ^ (value >> 8)

    return value

teststring = "test"

print "binascii calc:  0x%08x" % (binascii.crc32(teststring) & 0xffffffff)
print "zlib calc:      0x%08x" % (zlib.crc32(teststring) & 0xffffffff)
print "my calc:        0x%08x" % (crc32(teststring))

然后我得到以下输出:

binascii calc:  0xd87f7e0c
zlib calc:      0xd87f7e0c
my calc:        0x2780810c

binascii 和 zlib 计算结果一致,而我的计算结果不一致。我相信计算出的字节表是正确的,因为我已将其与网上可用的示例进行了比较。所以问题一定是计算每个字节的例程,有人能指出我正确的方向吗?

提前致谢!

【问题讨论】:

【参考方案1】:

我没有仔细查看您的代码,因此无法确定错误的确切来源,但您可以轻松调整它以获得所需的输出:

import binascii
from array import array

poly = 0xEDB88320

table = array('L')
for byte in range(256):
    crc = 0
    for bit in range(8):
        if (byte ^ crc) & 1:
            crc = (crc >> 1) ^ poly
        else:
            crc >>= 1
        byte >>= 1
    table.append(crc)

def crc32(string):
    value = 0xffffffffL
    for ch in string:
        value = table[(ord(ch) ^ value) & 0xff] ^ (value >> 8)

    return -1 - value

# test

data = (
    '',
    'test',
    'hello world',
    '1234',
    'A long string to test CRC32 functions',
)

for s in data:
    print repr(s)
    a = binascii.crc32(s)
    print '%08x' % (a & 0xffffffffL)
    b = crc32(s)
    print '%08x' % (b & 0xffffffffL)
    print

输出

''
00000000
00000000

'test'
d87f7e0c
d87f7e0c

'hello world'
0d4a1185
0d4a1185

'1234'
9be3e0a3
9be3e0a3

'A long string to test CRC32 functions'
d2d10e28
d2d10e28

这里还有几个测试可以验证调整后的crc32binascii.crc32 的结果相同。

from random import seed, randrange

print 'Single byte tests...',
for i in range(256):
        s = chr(i)
        a = binascii.crc32(s) & 0xffffffffL
        b = crc32(s) & 0xffffffffL
        assert a == b, (repr(s), a, b)

print('ok')

seed(42)

print 'Multi-byte tests...'
for width in range(2, 20):
    print 'Width', width
    r = range(width)
    for n in range(1000):
        s = ''.join([chr(randrange(256)) for i in r])
        a = binascii.crc32(s) & 0xffffffffL
        b = crc32(s) & 0xffffffffL
        assert a == b, (repr(s), a, b)
print('ok')

输出

Single byte tests... ok
Multi-byte tests...
Width 2
Width 3
Width 4
Width 5
Width 6
Width 7
Width 8
Width 9
Width 10
Width 11
Width 12
Width 13
Width 14
Width 15
Width 16
Width 17
Width 18
Width 19
ok

正如在 cmets 中所讨论的,原始代码中的错误来源是这种 CRC-32 算法反转了初始的 crc 缓冲区,然后反转了最终的缓冲区内容。所以value被初始化为0xffffffff而不是0,我们需要返回value ^ 0xffffffff,也可以写成~value & 0xffffffff,即取反value,然后选择结果的低32位.

【讨论】:

您先生是天赐之物,非常感谢您的快速回复和解决方案! @Cooper 不用担心。我对我的调整不是 100% 有信心(由于将算术与按位运算混合)。它似乎正确地完成了这项工作,但我有点担心它可能在某些极端情况下给出错误的答案。 OTOH,我刚刚检查了它在通过'\xff\xff\xff\xff' 时返回ffffffff,所以这是一个好兆头。 :) @Cooper 在这些额外的测试之后,我的信心增加了。 :) 如果它为任何输入返回错误的结果,我会非常感到惊讶。 看来,'return (value ^ 0xffffffff)' 将不再需要对结果进行 and 操作。按位算术不是我的强项,从那以后已经有一段时间了。再次感谢。 @Cooper 啊,当然! :) 另一个选项是return ~value & 0xffffffff。这两个都比return (-1 - value) & 0xffffffff 干净。您的版本可能是最好的,因为它使用的操作最少。

以上是关于Python中的CRC32计算而不使用库的主要内容,如果未能解决你的问题,请参考以下文章

CRC32 - 更改初始值

如何用 Python 计算 CRC32 以匹配在线结果?

python 通过crc32得到加密文件内容

关于python实现CRC32Mpeg2的应用和总结

python 计算CRC32

Python:在 zlib.crc32 中设置生成器多项式