原理参考这篇知乎: https://zhuanlan.zhihu.com/p/256487370
JAVA实现
应该是看了上篇文章就会用了哈。
package com.ws.utils.crc;
/**
* CRC校验配置信息
*/
public class CrcSettings {
//region Fields
/**
* 校验配置的名称
*/
public final String Name;
/**
* 位宽度
*/
public final int width;
/**
* 多项式值
*/
public final long Poly;
/**
* 初始值
*/
public final long Init;
/**
* 输入数据反转
*/
public final boolean RefIn;
/**
* 输出数据反转
*/
public final boolean RefOut;
/**
* 结果异或值
*/
public final long XorOut;
//endregion
/**
* 生成一组CRC校验配置信息实例
*
* @param name 校验方法名称
* @param width 位宽度
* @param poly 多项式值
* @param init 初始值
* @param refIn 是否对输入进行反转
* @param refOut 是否对输出进行反转
* @param xorOut 结果异或值
*/
public CrcSettings(String name, int width, long poly, long init, boolean refIn, boolean refOut, long xorOut) {
this.Name = name;
this.width = width;
this.Poly = poly;
this.Init = init;
this.RefIn = refIn;
this.RefOut = refOut;
this.XorOut = xorOut;
}
}
package com.ws.utils.crc;
/**
* 循环冗余校验(CRC)计算器类
*/
public class CrcCalculator {
//region Fields
/**
* 当前使用的配置方案
*/
public CrcSettings settings;
/**
* 位宽度
* <p>
* 一般情况下只使用8、16、32和64位。
*/
public byte bitWidth = 8;
/**
* 掩码
*/
private long mask = 0xFFFFFFFFFFFFFFFFL;
/**
* 记录生成的用于进行校验时查询的表
*/
private final long[] table = new long[256];
//endregion
//region Constructor
/**
* 生成一个循环冗余校验(CRC)计算器类实例
*
* @param settings CRC计算配置方案实例
*/
public CrcCalculator(CrcSettings settings) {
this.settings = settings;
bitWidth = (byte) this.settings.width;
if (bitWidth < 64) {
mask = (1L << bitWidth) - 1;
}
createTable();
}
//endregion
//region Public Methods
/**
* 计算CRC校验值
*
* @param data 待计算数据
* @return 计算结果
*/
public long calculate(byte[] data) {
return calculate(data, 0, data.length);
}
/**
* 计算CRC校验值
*
* @param data 待计算数据
* @param offset 数据偏移量
* @param length 数据长度
* @return 计算结果
*/
public long calculate(byte[] data, int offset, int length) {
long init = settings.RefOut ? reverseBits(settings.Init, bitWidth) : settings.Init;
long hash = computeCrc(init, data, offset, length);
return (hash ^ settings.XorOut) & mask;
}
/**
* 根据给定参数输出计算得出的Crc校验值
*
* @param init 初始值
* @param data 待计算数据
* @param offset 数据偏移量
* @param length 数据长度
* @return 计算结果
*/
public long computeCrc(long init, byte[] data, int offset, int length) {
long crc = init;
if (settings.RefOut) {
for (int i = offset; i < offset + length; ++i) {
crc = (table[(int) ((crc ^ data[i]) & 0xFF)] ^ (crc >>> 8));
crc &= mask;
}
} else {
int toRight = bitWidth - 8;
// toRight = toRight < 0 ? 0 : toRight;
if (toRight < 0) {
toRight = 0;
}
for (int i = offset; i < offset + length; ++i) {
crc = (table[(int) (((crc >> toRight) ^ data[i]) & 0xFF)] ^ (crc << 8));
crc &= mask;
}
}
return crc;
}
//endregion
//region Private Methods
/**
* 创建查询使用的表
*/
private void createTable() {
for (int i = 0; i < table.length; ++i) {
table[i] = createTableEntry(i);
}
}
/**
* 创建表项
*
* @param index 序号
* @return 表项值
*/
private long createTableEntry(int index) {
long r = index;
if (settings.RefIn) {
r = reverseBits(r, bitWidth);
} else if (bitWidth > 8) {
r <<= (bitWidth - 8);
}
long lastBit = (1L << (bitWidth - 1));
for (int i = 0; i < 8; ++i) {
if ((r & lastBit) != 0) {
r = ((r << 1) ^ settings.Poly);
} else {
r <<= 1;
}
}
if (settings.RefOut) {
r = reverseBits(r, bitWidth);
}
return r & mask;
}
/**
* 反转位
*
* @param ul 数值
* @param valueLength 数值长度
* @return 翻转后的结果
*/
private static long reverseBits(long ul, int valueLength) {
long newValue = 0;
for (int i = valueLength - 1; i >= 0; i--) {
newValue |= (ul & 1) << i;
ul >>= 1;
}
return newValue;
}
//endregion
}
手算CRC-8(不推荐)
这个是完全按照手算步骤在进行运算,所以只要懂CRC的人应该都能很好的理解这个类。
另外,如果想要直接求CRC,或者看一下每种CRC的区别,可以前往
CRC(循环冗余校验)在线计算
/**
* @Author ws
* @Date 2019/04/02
*/
public class CRC {
// 字节数组转二进制字符串
private static String toBinaryStr(byte[] bytes) {
StringBuilder ret = new StringBuilder(Integer.toBinaryString(bytes[0] & 0xff));
for (int i = 1; i < bytes.length; i++) {
StringBuilder s = new StringBuilder(Integer.toBinaryString(bytes[i] & 0xff));
while (s.length() < 8) {
s.insert(0, "0");
}
ret.append(s.toString());
}
ret.append("00000000");
return ret.toString();
}
// 二进制字符串转十进制整形
static int binStr2int(String bin) {
bin = bin.substring(bin.indexOf("1"));
int integ = 0;
for (int i = 0; i < bin.length(); i++) {
integ = integ * 2 + Integer.parseInt(String.valueOf(bin.charAt(i)));
}
return integ;
}
private static String getCRC(String bin) {
bin = bin.substring(bin.indexOf("1"));
// 校验多项式
String crcStr = "100000111";
if (bin.length() < crcStr.length()) {
return bin;
} else {
String devs = bin.substring(0, crcStr.length());
int i = binStr2int(devs) ^ binStr2int(crcStr);
StringBuilder sb = new StringBuilder(Integer.toBinaryString(i));
if (bin.length() != crcStr.length()) {
sb.append(bin.substring(crcStr.length()));
}
return getCRC(sb.toString());
}
}
// 获取CRC校验码字节
static byte findCRC(byte[] bytes) {
String bin = getCRC(toBinaryStr(bytes));
int i = ~binStr2int(bin);
i = i < 128 ? i : i - 256;
return (byte) i;
}
public static void main(String[] args) {
byte[] bytes = new byte[]{(byte) 0x80, (byte) 0xC9, (byte) 0x03, (byte) 0x06, (byte) 0x33};
System.out.println(CRC.findCRC(bytes));
}
}