我有一个用C ++编写的CRC16-CCITT算法的示例代码,我需要将其转换为Python的帮助。
示例C ++代码:
#include<iostream>
using namespace std;
unsigned short calculateCRC(unsigned char data[], unsigned int length)
{
unsigned int i;
unsigned short crc = 0;
for(i=0; i<length; i++){
crc = (unsigned char)(crc >>8) | (crc<<8);
crc ^= data[i];
crc ^= (unsigned char)(crc & 0xff) >> 4;
crc ^= crc << 12;
crc ^= (crc & 0x00ff) << 5;
}
return crc;
}
int main()
{
unsigned int length;
length = 15;
unsigned char data[length] = {0x01,0x08,0x00,0x93,0x50,0x2e,0x42,0x83,0x3e,0xf1,0x3f,0x48,0xb5,0x04,0xbb};
unsigned int crc;
crc = calculateCRC(data, length);
cout<< std::hex << crc << '\n';
}
此代码给出了9288作为正确的输出。
我在Python中尝试了以下操作:
#!/usr/bin/env python3
def calculateCRC(data):
crc = 0
for dat in data:
crc = (crc >> 8) or (crc << 8)
crc ^= dat
crc ^= (crc and 0xff) >> 4
crc ^= crc << 12
crc ^= (crc and 0x00ff) << 5
crc = hex(crc)
return (crc)
data = [0x01,0x08,0x00,0x93,0x50,0x2e,0x42,0x83,0x3e,0xf1,0x3f,0x48,0xb5,0x04,0xbb]
print(calculateCRC(data))
这将输出0xf988334b0799be2081。
能否请您帮助我了解我在做什么错? 谢谢。
答案 0 :(得分:6)
Python的int
类型是无界的,但是C / C ++ unsigned short
的值以2个字节表示,因此当您向左移动时会溢出。您需要在Python中添加masking来达到相同的效果,即删除高于16位最高有效位的任何位。仅当值向左移动时才需要这样做,因为向右移动已经丢弃了最右旋转的位。
接下来,您将|
and &
bitwise operators与or
and and
boolean logical operators混淆了。 C ++代码使用按位运算符,在Python中使用相同的运算符。
最后但并非最不重要的一点是,将转换为十六进制的内容留给调用者,不要在CRC函数本身中这样做:
UNSIGNED_SHORT_MASK = 0xFFFF # 2 bytes, 16 bits.
def calculateCRC(data):
crc = 0
for dat in data:
crc = (crc >> 8) | (crc << 8 & UNSIGNED_SHORT_MASK)
crc ^= dat
crc ^= (crc & 0xff) >> 4
crc ^= crc << 12 & UNSIGNED_SHORT_MASK
crc ^= (crc & 0x00ff) << 5
return crc
现在您得到相同的输出:
>>> print(format(calculateCRC(data), '04x'))
9288
我使用format()
function而不是hex()
来创建没有前缀0x
的十六进制输出。
正如Mark Adler正确指出的那样,我们不需要为每个左移操作都屏蔽;仅仅因为C / C ++操作自然会产生掩码值,并不意味着我们需要在这里经常这样做。每次迭代屏蔽一次就足够了:
def calculateCRC(data):
crc = 0
for dat in data:
crc = (crc >> 8) | (crc << 8)
crc ^= dat
crc ^= (crc & 0xFF) >> 4
crc ^= crc << 12
crc ^= (crc & 0x00FF) << 5
crc &= 0xFFFF
return crc
也许我们可以应用更多捷径来简化操作,从而加快操作速度,但是如果速度确实是一个问题,那么无论如何我还是会在Cython或C或其他本机编译的选项中重新实现
还请注意,您可以使用bytes
对象,而不必使用整数列表:
data = b'\x01\x08\x00\x93\x50\x2e\x42\x83\x3e\xf1\x3f\x48\xb5\x04\xbb'
像在C ++中使用bytes
数组那样,在char
对象上循环仍然会为您提供0到255之间的整数。
最后,您实际上不必自己翻译代码,您可以使用crccheck
之类的现有项目,该项目实现此特定的CRC16变体以及许多其他变体:
>>> from crccheck.crc import CrcXmodem
>>> print(format(CrcXmodem.calc(data), '04x'))
9288
crccheck
用纯Python编写。对于本机实现,有crcmod
。该库的文档有些缺乏,但是它也非常灵活和强大,并且实际上包括预定义的功能:
>>> from crcmod.predefined import mkPredefinedCrcFun
>>> xmodem = mkPredefinedCrcFun('xmodem')
>>> print(format(xmodem(data), '04x'))
9288