如何将CRC16-CCITT算法的C ++代码转换为Python代码?

时间:2019-03-25 12:19:37

标签: python c++ crc16

我有一个用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。

能否请您帮助我了解我在做什么错? 谢谢。

1 个答案:

答案 0 :(得分:6)

Python的int类型是无界的,但是C / C ++ unsigned short的值以2个字节表示,因此当您向左移动时会溢出。您需要在Python中添加masking来达到相同的效果,即删除高于16位最高有效位的任何位。仅当值向左移动时才需要这样做,因为向右移动已经丢弃了最右旋转的位。

接下来,您将| and & bitwise operatorsor 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