如何将 CRC16-CCITT 算法的 C++ 代码转换为 Python 代码?
How can I convert C++ code of a CRC16-CCITT algorithm to Python code?
我有一个用 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。
你能帮我理解我做错了什么吗?
谢谢。
Python 的 int
类型是无界的,但 C/C++ unsigned short
值以 2 个字节表示,因此当您向左移动时会溢出。您需要在 Python 中添加 masking 以达到相同的效果,其中您删除高于第 16 个最高有效位的任何位。这仅在值向左移动时才需要,因为右移已经删除了最右边的旋转位。
接下来,你在混淆|
and &
bitwise operators with 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'
遍历 bytes
对象仍然会为您提供 0 到 255 之间的整数,就像 C++ 中的 char
数组一样。
最后,您实际上不必自己翻译代码,您可以只使用像 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
我有一个用 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。
你能帮我理解我做错了什么吗? 谢谢。
Python 的 int
类型是无界的,但 C/C++ unsigned short
值以 2 个字节表示,因此当您向左移动时会溢出。您需要在 Python 中添加 masking 以达到相同的效果,其中您删除高于第 16 个最高有效位的任何位。这仅在值向左移动时才需要,因为右移已经删除了最右边的旋转位。
接下来,你在混淆|
and &
bitwise operators with 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'
遍历 bytes
对象仍然会为您提供 0 到 255 之间的整数,就像 C++ 中的 char
数组一样。
最后,您实际上不必自己翻译代码,您可以只使用像 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