JAVA获取CRC(循环冗余校验)

原理参考这篇知乎: 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));
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值