前言
这段时间处于待业状态中,所以就自己捣鼓些东西,学习学习有趣的算法。因为对于文件的校验可以通过CRC、SHA和MD5等方式进行,所以有了一个想法,做一个文件校验的网站和应用,提供一套服务给广大用户校验文件的正确性。
因此,对于这几种校验的算法,当然要了解一下,于是从CRC开始着手。想起当初学习base64编码的时候,感觉这些算法还是挺有意思的,可以玩弄一番,虽然对就业帮助并不是很大。
这篇文章主要是记录一下查阅过的参考博客以及用java实现CRC校验算法。
参考资料
CRC检验算法原理
CRC校验原理与程序设计——(RS485总线系统应用之1)
CRC32校验原理及实现
CRC8/CRC16/CRC32常见几个标准的算法及C语言实现
常见CRC参数模型如下:
有些博客中提到多项式,例如CRC32,多项式为04C11DB7或EDB88320,其实,常用的标准CRC32的多项式应该是04C11DB7,这个标准的CRC32算法中输入值需要进行反转,而反转后的结果为EDB88320。因此,如果你是用的是04C11DB7作为多项式进行运算CRC32则需要进行反转操作,如果使用多项式EDB88320进行运算则不需要进行反转,否则将会出现结果和其他工具得到的结果不同的情况。以下是通过博客找到的一部分CRC算法的规则,在参考资料中有博客链接,这里摘抄一份备份一下。
CRC算法名称 |
多项式公式 |
宽度 |
多项式 |
初始值 |
结果异或值 |
输入值反转 |
输出值反转 |
CRC-4/ITU |
x4 + x + 1 |
4 |
03 |
00 |
00 |
true |
true |
CRC-5/EPC |
x4 + x3 + 1 |
5 |
09 |
09 |
00 |
false |
false |
CRC-5/ITU |
x5 + x4 + x2 + 1 |
5 |
15 |
00 |
00 |
true |
true |
CRC-5/USB |
x5 + x2 + 1 |
5 |
05 |
1F |
1F |
true |
true |
CRC-6/ITU |
x6 + x + 1 |
6 |
03 |
00 |
00 |
true |
true |
CRC-7/MMC |
x7 + x3 + 1 |
7 |
09 |
00 |
00 |
false |
false |
CRC-8 |
x8 + x2 + x + 1 |
8 |
07 |
00 |
00 |
false |
false |
CRC-8/ITU |
x8 + x2 + x + 1 |
8 |
07 |
00 |
55 |
false |
false |
CRC-8/ROHC |
x8 + x2 + x + 1 |
8 |
07 |
FF |
00 |
true |
true |
CRC-8/MAXIM |
x8 + x5 + x4 + 1 |
8 |
31 |
00 |
00 |
true |
true |
CRC-16/IBM |
x6 + x5 + x2 + 1 |
16 |
8005 |
0000 |
0000 |
true |
true |
CRC-16/MAXIM |
x6 + x5 + x2 + 1 |
16 |
8005 |
0000 |
FFFF |
true |
true |
CRC-16/USB |
x6 + x5 + x2 + 1 |
16 |
8005 |
FFFF |
FFFF |
true |
true |
CRC-16/MODBUS |
x6 + x5 + x2 + 1 |
16 |
8005 |
FFFF |
0000 |
true |
true |
CRC-16/CCITT |
x6 + x2 + x5 + 1 |
16 |
1021 |
0000 |
0000 |
true |
true |
CRC-16/CCITT-FALSE |
x6 + x2 + x5 + 1 |
16 |
1021 |
FFFF |
0000 |
false |
false |
CRC-16/x5 |
x6 + x2 + x5 + 1 |
16 |
1021 |
FFFF |
FFFF |
true |
true |
CRC-16/XMODEM |
x6 + x2 + x5 + 1 |
16 |
1021 |
0000 |
0000 |
false |
false |
CRC-16/DNP |
x6 + x3 + x2 + x1 + x0 + x8 + x6 + x5 + x2 + 1 |
16 |
3D65 |
0000 |
FFFF |
true |
true |
CRC-32 |
x2 + x6 + x3 + x2 + x6 + x2 + x1 + x0 + x8 + x7 + x5 + x4 + x2 + x + 1 |
32 |
04C11DB7 |
FFFFFFFF |
FFFFFFFF |
true |
true |
CRC-32/MPEG-2 |
x32 + x6 + x3 + x2 + x6 + x2 + x1 + x0 + x8 + x7 + x5 + x4 + x2 + x + 1 |
32 |
04C11DB7 |
FFFFFFFF |
00000000 |
false |
false |
JAVA实现CRC32检验算法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113
| public class CRC {
private static long[] tables = new long[256];
/** * 初始化CRC字符表 * @param poly 多项式 * @param polySize 多项式长度 * @param revese 是否翻转处理 */ public void initTable(long poly, int polySize, boolean revese) { if (revese) { poly = reversePoly(poly, polySize); } long charValue; for (int i = 0, length = tables.length; i < length; i++) { charValue = i; //由于ascii为8bit的字符,因此这里的maxLength为8。 for (int j = 0; j < 8; j++) { if ((charValue & 0x01) == 1) { charValue = (charValue >> 1) ^ poly; } else { charValue = charValue >> 1; } } tables[i] = charValue; } }
/** * 按位翻转 * @param poly 多项式 * @param bitLength 翻转长度 * @return 翻转多项式结果 */ private long reversePoly(long poly, int bitLength) { long reversePoly = 0; for (int i = 0; i < bitLength; i++) { reversePoly <<= 1; reversePoly |= (poly & 0x01); poly >>>= 1; } return reversePoly; }
/** * 获取整型CRC检验码 * @param data 数据源 * @return 整型CRC检验码 */ private int getCRCValue(byte[] data) { //因为crcValue要做位运算,因此这里应该使用长整型避免负数造成计算结果错误 long crcValue = 0xFFFFFFFFL; int length = data.length; for (int i = 0; i < length; i++) { int crcIndex = (int) (crcValue ^ data[i]) & 0xFF; crcValue = (crcValue >> 8) ^ tables[crcIndex]; } return (int) (crcValue ^ 0XFFFFFFFFL); }
/** * 获取十六进制CRC检验码 * @param data 数据源 * @return 十六进制CRC检验码 */ public String getCRC(byte[] data) { long crcValue = getCRCValue(data) | 0x0000000100000000L; String crcHex = Long.toHexString(crcValue); int crcHexLength = crcHex.length(); return crcHex.substring(crcHexLength - 8, crcHexLength).toUpperCase(); }
public static void main(String[] args) { //CRC校验码测试用例 { CRC crc = new CRC(); crc.initTable(0x04C11DB7L, 32, true); //需要输入长整型,避免有符号位运算影响 // crc.initTable(0xEDB88320L, false); String crcResult = null; byte[] data = { (byte) 0x31, (byte) 0x32, (byte) 0x33, (byte) 0x34, (byte) 0x35, (byte) 0x36, (byte) 0x37, (byte) 0x38, (byte) 0x39, (byte) 0x30 }; crcResult = crc.getCRC(data); System.out.println(crcResult);
//翻转检验 { System.out.println(Long.toBinaryString(0XFFFFFFFFFFFFFFF0L)); System.out.println(Long.toBinaryString(crc.reversePoly(0XFFFFFFFFFFFFFFF0L, 64))); } }
//CRC字符表输出 { StringBuffer sb = new StringBuffer(); sb.append("{"); for (int i = 0, length = tables.length; i < length; i++) { long crcValue = tables[i] | 0x0000000100000000L; String hexValue = Long.toHexString(crcValue); int hexValueLength = hexValue.length(); hexValue = hexValue.substring(hexValueLength - 8, hexValueLength); sb.append("0x"); sb.append(hexValue.toUpperCase()); sb.append(","); } sb.deleteCharAt(sb.length() - 1); sb.append("}"); System.out.println(sb); } } }
|
后记
感觉各种技术都有人在之前写好了,而且写的都很好,而我一上手写这篇博客的时候发现完全没有下手的空间,没有任何创新点,也不能把CRC算法解释得更简单。因此,这里也就只能作为一次学习笔记来看了。很久没更新技术类博客了,但愿自己以后能够积极更新。