[Java SE] 基础工具类:ByteUtils(字节操作)

0 引言

  • 与嵌入式软件数据交互过程中,必然涉及各种的、大量的字节操作场景。如:16进制与10进制、2进制间的转换,字符串、byte数组与int之间的转换等。故此有此核心工具类的沉淀。

1 ByteUtils

依赖

<properties>
	<!-- 编程提效工具 -->
	<lombok.version>1.18.22</lombok.version>

	<!-- 日志 -->
	<slf4j.version>1.7.25</slf4j.version>
	<log4j.version>2.20.0</log4j.version>

	<!-- 其他依赖 -->
	<commons.lang3.version>3.9</commons.lang3.version>
	<commons.io.version>2.11.0</commons.io.version>
	<commons.codec.version>1.13</commons.codec.version>
</properties>	

<dependencies>
	<!-- language enhance / 语言增强 | start -->
	<dependency>
		<groupId>org.projectlombok</groupId>
		<artifactId>lombok</artifactId>
		<version>${lombok.version}</version>
		<scope>provided</scope>
	</dependency>

	<dependency>
		<groupId>org.apache.commons</groupId>
		<artifactId>commons-lang3</artifactId>
		<version>${commons.lang3.version}</version>
		<scope>provided</scope>
	</dependency>

	<dependency>
		<groupId>commons-io</groupId>
		<artifactId>commons-io</artifactId>
		<version>${commons.io.version}</version>
		<scope>compile</scope>
	</dependency>

	<dependency>
		<groupId>commons-codec</groupId>
		<artifactId>commons-codec</artifactId>
		<version>${commons.codec.version}</version>
		<scope>provided</scope>
	</dependency>
	<!-- language enhance / 语言增强 | end -->


	<!-- log | start -->
	<dependency>
		<groupId>org.slf4j</groupId>
		<artifactId>slf4j-api</artifactId>
		<version>${slf4j.version}</version>
		<scope>provided</scope>
	</dependency>
	<dependency>
		<groupId>org.apache.logging.log4j</groupId>
		<artifactId>log4j-api</artifactId>
		<version>${log4j.version}</version>
		<scope>provided</scope>
	</dependency>
	<dependency>
		<groupId>org.apache.logging.log4j</groupId>
		<artifactId>log4j-core</artifactId>
		<version>${log4j.version}</version>
		<scope>provided</scope>
	</dependency>
	<!-- this dependency is required for data desensitization -->
	<dependency>
		<groupId>org.apache.logging.log4j</groupId>
		<artifactId>log4j-slf4j-impl</artifactId>
		<version>${log4j.version}</version>
		<scope>provided</scope>
	</dependency>
	<!-- log | end -->
</dependencies>

ByteUtils

package com.xxx.sdk.utils.bytes;

//import com.xxx.sdk.utils.dbc.DbcReader;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.DecoderException;
import org.apache.commons.codec.binary.Hex;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.core.config.plugins.validation.constraints.NotBlank;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import jakarta.xml.bind.DatatypeConverter;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;

/**
 * 字节操作工具类
 * @description
 *     与嵌入式软件数据交互过程中,必然涉及各种的、大量的字节操作场景。
 *     如:16进制与10进制、2进制间的转换,字符串、byte数组与int之间的转换等,故此有此核心工具类的沉淀。
 * @version v1.0
 * @update-time 2025/07/18 11:32
 * @description ...
 */
@Slf4j
public class BytesUtils {
    /**
     * 单个字节的位长
     * 1byte = 8 bit
     * 1个16进制位 占 4 bit => 1个字节可代表 2 个 16进制位, 1 个16进制位 = 0.5 个字节
     **/
    public final static int BYTE_BIT_LENGTH = 8;

    /**
     * 0x00 占位符 的 UTF8 表示
     * 0x00 (在 ASCII 表中表示 NULL 字符)
     **/
    public final static String PLACEHOLDER_UTF8_0X00 = "\u0000";
    /** 0x00 占位符 的 16进制 表示 **/
    public final static String PLACEHOLDER_HEX_0X00 = "0x00";

    public final static Charset CHARSET_DEFAULT = StandardCharsets.UTF_8;

    public static String INTEGER_ARRAY_STRING_SEPARATOR = ".";//整型数组字符换的分隔符

    /**
     * 移除十六进制字符串中的 "0x"
     * @param hexStringWith0x
     * 1. eg: "0x5AB"
     * @return
     * 1. eg: "5AB"
     */
    public static String remove0xForHexString(String hexStringWith0x){
        return hexStringWith0x.replace("0x", "");
    }

    /**
     * 判断对象是否为 byte 数组
     * @note 补充 byteArrayToString
     *  byte 数组转字符串的方法: new String( new String( (byte []) javaObject) , "UTF-8" )
     * @reference-doc
     *  [1] java判断object是否为byte数组 - 51CTO - https://blog.51cto.com/u_16213452/11581409
     * @param object
     * @return
     */
    public static Boolean isByteArray(Object object){
        if (object == null) {
            return false; // 如果对象为null,则直接返回false
        }
        if (!object.getClass().isArray()) {//判断是否为数组
            return false; // 如果对象不是数组,则直接返回false
        }
        //if (object.getClass().getComponentType() != byte.class) {//方法1
        if(!object.getClass().getComponentType().getCanonicalName().equalsIgnoreCase("byte")) {//方法2
            return false; // 如果数组的组件类型不是byte,则返回false
        }
        return true; // 如果数组的组件类型是byte,则返回true
    }

    /** 合并2个字节数组 **/
    public static byte[] merge(byte[] array1, byte[] array2) {
        byte[] combined = new byte[array1.length + array2.length];
        System.arraycopy(array1, 0, combined, 0, array1.length);
        System.arraycopy(array2, 0, combined, array1.length, array2.length);
        return combined;
    }

    /**
     * 字节转 无符号整数
     * @param aByte
     * @return
     * @usage
     *   byte b = (byte) 0xc4;
     *   toUnsignedInt(b) = 196
     */
    public static int toUnsignedDecimal(byte aByte){
        return Byte.toUnsignedInt(aByte);
    }

    /**
     * X进制数的字符串转10进制的有符号整型
     * @return
     * @sample
     *  radixNumberStringToDecimal("01070500", 16) = 17237248
     *  radixNumberStringToDecimal("1070500", 16) = 17237248
     *  radixNumberStringToDecimal("1A3F", 16) = 6719
     *  radixNumberStringToDecimal("-1A3F", 16) = -6719
     *  radixNumberStringToDecimal("FFFFFFFFFFFFFFE1", 16) : 将报错
     */
    public static Long radixNumberStringToDecimal(String xRedixNumberString, int radix){
        return Long.parseLong(xRedixNumberString, radix);
    }

    /**
     * X进制数的字符串转10进制的无符号整型
     * @return
     * @sample
     *  radixNumberStringToUnsignedDecimal("01070500", 16) = 17237248
     *  radixNumberStringToUnsignedDecimal("1070500", 16) = 17237248
     *  radixNumberStringToUnsignedDecimal("1A3F", 16) = 6719
     *  radixNumberStringToUnsignedDecimal("FFFFFFFFFFFFFFE1", 16) = -31
     */
    public static Long radixNumberStringToUnsignedDecimal(String xRedixNumberString, int radix){
        return Long.parseUnsignedLong(xRedixNumberString, radix);
    }

    /**
     * 清除十六进制字符串前缀的NULL字符(0x00)
     * @param hexString eg: "00004856"
     * @return eg: "4856"
     */
    public static String clearHexStringPrefixNullCharacters(String hexString){
        // 1. 输入验证(略,根据实际情况决定是否需要严格验证)

        // 使用StringBuilder来避免不必要的字符串拷贝
        StringBuilder sb = new StringBuilder();

        char [] hexCharArray = hexString.toCharArray();// ['0', '0', '0', '0', '4', '8', '5', '6']
        int byteSize = hexCharArray.length/2;
        char [] hexByte = new char[] { 0x00, 0x00 };//新建 2个 NULL 字节 的数组
        for(int i=0; i<byteSize; i++){
            hexByte[0] = hexCharArray[i*2];//i*2 + 0
            hexByte[1] = hexCharArray[i*2 + 1];//i*2 + 1
            if(hexByte[0] == '0' && hexByte[1] == '0') {//当前字节为 0x00 : 被清理掉,继续下移到下一个字节
                continue;
            } else {//循环终止,截取剩余的字符 即 清理的最终结果
                sb.append(hexCharArray, i*2, hexCharArray.length -2*i );
                /**
                 * 底层调用 : System.arraycopy(var1=hexCharArray, var2=i*2, this.value, this.count=0, var3=length);
                 * @demo
                 *  byte[] originalBytes={1,2,3,4,5,6};
                 *  byte[] targetBytes = {7,8,9};
                 *  System.arraycopy(originalBytes, 2, targetBytes , 0, 3); //从源数组下标为2的元素开始,复制3个元素。然后以目标数组下标为0的元素为起点,开始拷贝
                 *  运算结果 : [3,4,5]
                 */
                break;
            }
        }

        return sb.toString(); // 返回处理后的字符串
    }

    /**
     * 字节数组转 整型数组(以字符串组合而成)
     * @param hexString
     * @param separator
     * @return null or integerArrayString
     * @usage "00102030","." => "1.2.3"
     */
    public static String hexStringToIntegerArrayString(String hexString, String separator){
        if(StringUtils.isEmpty(hexString) ){// null or ""
            return null;
        }
        if(hexString.length()%2 != 0){//字节数据残缺
            throw new RuntimeException("Fail to convert to integer array string cause that hex string data is abnormal!hexString:" + hexString);
        }
        separator = ObjectUtils.isEmpty(separator)?"":separator;
        int byteSize = hexString.length()/2;
        StringBuilder resultBuilder = new StringBuilder();
        for(int i=0;i<byteSize;i++){
            String hexByteStr = hexString.substring(i*2,i*2 + 2); //单个字节的16进制表示 ; eg: "48"
            resultBuilder.append(Integer.parseInt( hexByteStr, 16) );// eg: "48" => 72
            resultBuilder.append(separator);
        }
        //删除最后一个多余的字符
        resultBuilder.deleteCharAt(resultBuilder.length()-1);
        resultBuilder.toString();
        return resultBuilder.toString();
    }

    public static String hexStringToIntegerArrayString(String hexString){
        return hexStringToIntegerArrayString(hexString, INTEGER_ARRAY_STRING_SEPARATOR);
    }

    public static Integer [] hexStringToLongArray(String hexString){
        if(StringUtils.isEmpty(hexString) ){// null or ""
            return null;
        }
        if(hexString.length()%2 != 0){//字节数据残缺
            throw new RuntimeException("Fail to convert to integer array string cause that hex string data is abnormal!hexString:" + hexString);
        }
        int byteSize = hexString.length()/2;
        Integer [] integers = new Integer[] { byteSize };
        StringBuilder resultBuilder = new StringBuilder();
        for(int i=0;i<byteSize;i++){
            String hexByteStr = hexString.substring(i*2,i*2 + 2); //单个字节的16进制表示 ; eg: "48"
            integers[i] = Integer.parseInt( hexByteStr, 16);// "48" => 72
        }
        return integers;
    }

    /**
     * 字节转二进制字符串
     * @note
     *  单个字节 : 'a' => 97 => 0x61 => 01100001 (二进制) | 兼容 : 无符号数、有符号数
     *      byteToBit( (byte) 0x61 ) = "01100001"
     *  单个字节 : 250 => 0xFA = 11111010 (二进制) | 无符号数
     *      byteToBit( (byte) 0xFA ) = "11111010"
     * @param b
     * @return
     */
    public static String byteToBinaryString(byte b) {
        return "" +
            (byte) ((b >> 7) & 0x1) +
            (byte) ((b >> 6) & 0x1) +
            (byte) ((b >> 5) & 0x1) +
            (byte) ((b >> 4) & 0x1) +
            (byte) ((b >> 3) & 0x1) +
            (byte) ((b >> 2) & 0x1) +
            (byte) ((b >> 1) & 0x1) +
            (byte) ((b >> 0) & 0x1);
    }

    /**
     * 十六进制字符串 转 二进制字符串
     * @note
     *  hexStringToBinaryString( "61" ) = "01100001" | 'a' => 97 => 0x61 => "01100001"
     *  hexStringToBinaryString( "FA" ) = "11111010"
     *  hexStringToBinaryString( "48656c6c6f" ) = "0100100001100101011011000110110001101111"
     *      即 "01001000 01100101 01101100 01101100 01101111"
     * @param hexString
     * @return
     */
    public static String hexStringToBinaryString(String hexString) {
        byte[] bytes = hexStringToBytes(hexString);
        StringBuilder s = new StringBuilder(64);
        for (byte aByte : bytes) {
            s.append(BytesUtils.byteToBinaryString(aByte));
        }
        return s.toString();
    }
    /**
    public static String hexStringToBinaryString(String hexString){
        if(ObjectUtils.isEmpty(hexString) || hexString.length()%2 != 0){
            return "";
        }
        StringBuilder binaryString = new StringBuilder();
        String tmp;
        for(int i=0;i<hexString.length(); i++){
            tmp = "0000" + Integer.toBinaryString( Integer.parseInt(hexString.substring(i, i+1), 16) );
            binaryString.append( tmp.substring( tmp.length() -4 ) );

            //String binary = Integer.toBinaryString(Integer.parseInt(hexString.charAt(i) + "", 16));
            //binaryBuilder.append(String.format("%4s", binary).replace(' ', '0')); // 格式化为4位二进制数
        }

        logger.info("hexString : {}, binaryString : {}", hexString, binaryString);
        return binaryString.toString();
    }
    **/

    /**
     * 字节数组转十六进制表示的字符串
     * @note
     *  'H' => 72 => 0x48 ; 'e' => 101 => 0x65
     *  bytesToHexString(new byte[] { 0x48, 0x65 }) = "4865"
     * @param byteArray
     * @return
     */
    public static String bytesToHexString(byte[] byteArray) {
        StringBuilder builder = new StringBuilder();
        if (byteArray == null || byteArray.length <= 0) {
            return null;
        }
        String hv;
        for (int i = 0; i < byteArray.length; i++) {
            // 以十六进制(基数 16)无符号整数形式返回一个整数参数的字符串表示形式,并转换为大写
            hv = Integer.toHexString(byteArray[i] & 0xFF);
            if (hv.length() < 2) {
                builder.append(0);
            }
            builder.append(hv);
        }
        return builder.toString();
    }

    //public static String byteToHexString(byte aByte) {
    //    byte [] bytes = new byte[] { aByte };
    //    return bytesToHexString( bytes );
    //}

    /**
    public static String bytesToHexString(byte[] byteArray) {
        StringBuilder stringBuilder = new StringBuilder("");
        if (byteArray != null) {
            for (int i = 0; i < byteArray.length; ++i) {
                stringBuilder.append( byteToHexString(byteArray[i]) );
            }
        }
        return stringBuilder.toString();
    }
    **/

    /**
     * 字符串转字节数组
     * @note
     *  stringToBytes("Hello World", Charset.forName("UTF-8")) => [72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100]
     *  stringToBytes("娃哈哈", Charset.forName("UTF-8")) => [-27, -88, -125, -27, -109, -120, -27, -109, -120]
     * @param text
     * @return
     */
    public static byte [] stringToBytes(String text, Charset charset){
        if(ObjectUtils.isEmpty(text)){
            throw new RuntimeException("text is empty! text : " + text);
        }
        return charset==null?text.getBytes():text.getBytes(charset);
    }

    public static String bytesToString(byte [] bytes, Charset charset){
        String textString = new String( bytes, charset );
        return textString;
    }

    /**
     * (多字节的)二进制字符串转二进制数组
     * @note <p>需了解本方法的局限性</p>
     *  <p>单个字节的二进制字符串 必须满足 java byte(8bit、有符号数) 的取值范围 [-128, 127]</p>
     *  <p>正确示例: binaryStringToBytes("01001000") => out: [72]</p>
     *  <p>错误示例: binaryStringToBytes("11111010") => 0xFA = 250 = 0b1111 0b00001010" => out: [-6] (错误结果,因:超出有符号数byte取值范围)</p>
     * @param binaryString
     * @return
     */
    public static byte[] binaryStringToBytes(String binaryString) {
        if(ObjectUtils.isEmpty(binaryString)){
            throw new RuntimeException("Fail to convert binary array cause by the empty binary string! binaryString : " + binaryString);
        }
        if(binaryString.length() %BYTE_BIT_LENGTH != 0){//不是4的倍数
            throw new RuntimeException("Fail to convert binary array cause that the binary string is not a multiple of " + BYTE_BIT_LENGTH + "! binaryString : " + binaryString);
        }

//        char [] charArray =  binaryString.toCharArray() // string 内部由 2个字节的char组成的 char 数组 , 故: 这种转换做法有风险
//        byte [] binaryArray = new byte [ binaryString.length() ];
//        for (int i = 0; i < charArray.length; i ++) {
//            //binaryArray[i] = (byte)charArray[i]; // java char 占用 : 2个字节 ; java byte 占用 1个字节 => 这种做法不正确
//            binaryArray[i]
//        }

        int byteSize = binaryString.length()/BYTE_BIT_LENGTH;
        byte[] binaryArray = new byte[byteSize];
        for (int i = 0; i < byteSize; i ++) {
            String byteBinaryStr = binaryString.substring(i*BYTE_BIT_LENGTH, i*BYTE_BIT_LENGTH + BYTE_BIT_LENGTH);//sample "01001000"
            binaryArray[i] = binaryStringToByte(byteBinaryStr);
        }
        return binaryArray;
    }

    /**
     * (多字节的)二进制字符串转 ByteBuffer
     * @note 解决 java byte 类型仅支持有符号数 的 取值范围 [-128, 127] 的局限性 (ByteBuffer : 支持无符号数)
     * 在Java中,如果你想要处理无符号的byte数组,你可以使用ByteBuffer类,它可以以大端或小端格式读写字节。
     * 由于Java中的byte类型是有符号的,范围为-128到127,所以无符号的byte数组需要特别处理。
     * 例如:
     *      ByteBuffer buffer = ByteBuffer.allocate(5);// 创建一个包含无符号byte的ByteBuffer
     *      buffer.put((byte) 0x80); // 256的二进制补码表示为1000 0000,是一个负数 | 注意,put 方法 传入的是 byte 值,它们会以其补码形式存储。
     *      buffer.put((byte) 0xFF); // 255的二进制补码表示为1111 1111
     *      buffer.flip(); // 翻转Buffer,准备读取
     *      // 读取无符号 byte
     *      int unsignedByte1 = buffer.get() & 0xFF; // 将有符号byte转换为无符号 : 256
     *      int unsignedByte2 = buffer.get() & 0xFF; // 将有符号byte转换为无符号 : 255
     * 当我们想要读取这些值时,我们使用 get() 方法,并将得到的值与255进行"与"操作(0xFF),这样可以确保我们得到无符号的值。
     * 这种方法适用于处理单个 byte 值,如果你需要处理更大的无符号数,你可能需要多次调用get()方法,并进行适当的位操作来组成一个无符号整数。
     * @usage
     *   ByteBuffer byteBuffer = binaryStringToByteBuffer("1111101011111011");//0xFA , 0xFB
     *   byteBuffer.flip();
     *   int value1 = byteBuffer.get(0) & 0xFF;//250
     *   int value2 = byteBuffer.get(1) & 0xFF;//251
     * @param binaryString
     * @return
     */
    public static ByteBuffer binaryStringToByteBuffer(String binaryString) {
        int byteSize = binaryString.length()/BYTE_BIT_LENGTH;
        if(binaryString.length() % BYTE_BIT_LENGTH != 0){
            throw new RuntimeException("the binary string must be `" + "`'s times!");
        }

        // 创建一个包含 无符号 byte 的 ByteBuffer
        ByteBuffer buffer = ByteBuffer.allocate(byteSize);
        for(int i=0;i<byteSize;i++){
            String oneByteBinaryString =  binaryString.substring(i*BYTE_BIT_LENGTH, i*BYTE_BIT_LENGTH + BYTE_BIT_LENGTH);//sample "01001000"
            int value = oneByteBinaryStringToInt(oneByteBinaryString);
            buffer.put( (byte) value );
        }
        return buffer;
    }

    /**
     * 单字节的二进制字符串转 Int 整型
     * @sample
     *  oneByteBinaryStringToInt("1010") => out : 10 => 0xA
     *  oneByteBinaryStringToInt("11111010") => out : 250 => 0xFA
     * @param oneByteBinaryString
     * @return
     */
    public static Integer oneByteBinaryStringToInt(String oneByteBinaryString){
        Integer value = Integer.parseInt(oneByteBinaryString, 2); // 转换为十进制整数
        return value;
    }

    /**
     * 将位长为8位的二进制字符串转为 1个字节
     * @note
     *  注意避坑 : java byte 的局限性
     *      1. byte 取值范围是 [-128, +127]. Java中的 byte 数据类型是 8 位、有符号的,其取值范围是从 -128 到 127
     *      2. 基于1,入参(`binaryString`) 的取值范围 : [0b10000000(即-128) , 0b11111111(即-1)] U [0b00000000, 0b01111111]
     * @param binaryString
     *  sample "01001000" => 72 ; "01111111" => 127
     * @return
     */
    public static byte binaryStringToByte(String binaryString){
        int x = 0;// 注 : int 占用 4 个字节
        for(int j=0; j < BYTE_BIT_LENGTH; j++){
            String currentBinaryBit = binaryString.substring(BYTE_BIT_LENGTH-1-j, (BYTE_BIT_LENGTH-1-j)+1);
            x = x + Integer.parseInt( currentBinaryBit ) * ( (int) Math.pow(2, j) );//强制类型转换会直接截断小数部分
        }
        return (byte) x;
    }

    /**
     * 二进制字符串转十六进制字符串
     * @param binaryString
     * @sample binaryStringToHexString("1111101011111011") => "FAFB" (即: 0xFA, 0xFB)
     *
     */
    public static String binaryStringToHexString(String binaryString){
        byte[] binaryArray = binaryStringToBytes(binaryString);

        StringBuilder hexStr = new StringBuilder();
        int byteSize = binaryArray.length;
        for(int i=0; i< byteSize; i++){
            hexStr.append( byteToHexString( binaryArray[i] ) );
        }
        return hexStr.toString();
    }

    /**
     * char 转 byte 数组[固定2个字节]
     * @description 背景 : char类型即可以存储英文字母,也可存储汉字;汉字在java中使用Unicode编码占两个字节
     * @param c
     * @return
     * @sample
     *  charTo2Bytes('G') = [0, 71]
     *  charTo2Bytes('哇') = [84, -57]
     */
    public static byte[] charTo2Bytes(char c) {
        byte[] b = new byte[2];
        b[0] = (byte) ((c & 0xFF00) >> 8);
        b[1] = (byte) (c & 0xFF);
        return b;
    }

    /**
     * 十六进制字符串转byte数组
     * @param hexString | sample "48656c6c6f"
     * @return | sample [72, 101, 108, 108, 111] 即 [0x48, 0x65, 0x6c, 0x6c, 0x6f ] //注 : 以0x开始的数据表示16进制
     */
    /**
     * 将Hex String转换为Byte数组
     * @param hexString the hex string
     * @return the byte [ ]
     */
    public static byte[] hexStringToBytes(String hexString) {
        if (StringUtils.isBlank(hexString)) {
            return null;
        }
        //hexString = hexString.toLowerCase();
        final byte[] byteArray = new byte[hexString.length() >> 1];
        int index = 0;
        for (int i = 0; i < hexString.length(); i++) {
            if (index > hexString.length() - 1) {
                return byteArray;
            }
            byte highDit = (byte) (Character.digit(hexString.charAt(index), 16) & 0xFF);
            byte lowDit = (byte) (Character.digit(hexString.charAt(index + 1), 16) & 0xFF);
            byteArray[i] = (byte) ((highDit << 4) | (lowDit & 0xFF));
            index += 2;
        }
        return byteArray;
    }
    /**
    public static byte[] hexStringToBytes(String hexString){
        //String hexString = "48656c6c6f"; // 示例十六进制字符串 "Hello"
        byte[] byteArray = DatatypeConverter.parseHexBinary(hexString);
        return byteArray;
    }
    **/

    /**
     * 十六进制字符串转字节数组 | 方法2
     * @note 局限性 : byte 的取值范围 : [-128, 127]
     *  例如 错误的入参示范 :  hexStringToBytes("fffafb") = [-1, -6, -5] (错误结果)
     * @param hexStr
     * @return
     */
    /**
    public static byte[] hexStringToBytes(String hexStr) {
        if (!StringUtils.isEmpty(hexStr) && hexStr.length() % 2 == 0) {
            byte[] data = null;

            try {
                char[] chars = hexStr.toCharArray();
                data = Hex.decodeHex(chars);
            } catch (DecoderException var3) {
                logger.error(var3.getMessage(), var3);
            }

            return data;
        } else {
            return null;
        }
    }
    **/

    /**
     * 字节数组 转 十六进制数组字符串
     * @usage bytesToHexArrayString( new byte [] { 0x34, 0x67} ) = "[0x34,0x67]"
     * @param byteArray
     * @return
     */
    public static String bytesToHexArrayString(byte[] byteArray) {
        StringBuffer sb = new StringBuffer();
        sb.append("[");
        if (byteArray != null) {
            for (int i = 0; i < byteArray.length; ++i) {
                sb.append("0x").append(byteToHexString(byteArray[i]));
                if (i < byteArray.length - 1) {
                    sb.append(",");
                }
            }
        }

        sb.append("]");
        return sb.toString();
    }

    /**
     * 字节转16进制字符串
     * @sample byteToHexString( (byte)0xFA ) = "FA"
     * @param aByte
     * @return
     */
    public static String byteToHexString(byte aByte) {
        char[] digit = new char[]{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
        char[] tempArr = new char[]{digit[aByte >>> 4 & 15], digit[aByte & 15]};
        String s = new String(tempArr);
        return s;
    }

    /**
     * INT 转 16进制字符串
     * @param v
     * @return
     * @usage
     *  intToHexStringWithoutZeroPrefix(255) => "ff"
     *  intToHexStringWithoutZeroPrefix(13) => "0d"
     * @sample
     *   如: 0x00 04 80 05 (294917:DEC) 报文帧 => messageIdHex = "48005" (00 04 80 05 前面的 几个 0 会被移除)
     *   String messageIdHex = Integer.toHexString( new Integer(294917) ).toLowerCase();
     */
    public static String intToHexStringWithoutZeroPrefix(int v) {
        String hexStr = Integer.toHexString(v);
        if (hexStr.length() % 2 != 0) {
            hexStr = "0" + hexStr;
        }

        return hexStr;
    }

    /**
     * INT 转 16进制字符串
     * @param v
     * @return
     * @usage
     *  intToHexStringWithZeroPrefix(255) => "ff"
     * @sample
     *   如: 0x00 04 80 05 (294917:DEC) 报文帧 => messageIdHex = "00048005" (00 04 80 05 前面的 几个 0 不会被移除)
     *   String messageIdHex = Integer.toHexString( new Integer(294917) ).toLowerCase();
     */
    public static String intToHexStringWithZeroPrefix(int v) {
        byte [] bytes = BytesUtils.intToBytes(v);
        String hexString = bytesToHexString(bytes);
        return hexString;
    }

    /**
     * LONG 转 16进制字符串
     * @description
     *  Long.toHexString( 2418704398L ) = "902a800e"
     * @param v
     * @return
     * @usage
     *  longToHexStringWithZeroPrefix(255) => "ff"
     * @sample
     *   如: 0x00 04 80 05 (294917:DEC) 报文帧 => messageIdHex = "00048005" (00 04 80 05 前面的 几个 0 不会被移除)
     *   String messageIdHex = Integer.toHexString( new Integer(294917) ).toLowerCase();
     */
    public static String longToHexStringWithZeroPrefix(long v) {
        byte [] bytes = BytesUtils.longToBytes(v);
        String hexString = bytesToHexString(bytes);
        return hexString;
    }

    /**
     * 16进制转换成为文本字符串
     * @usage
     *   //含 BOM
     *   hexStringToTextString(" efbbbf68656c6c6f20776f726c6421e4bda0e5a5bd21 ", Charset.forName ( " UTF - 8 ")) //"hello world!你好!"
     *   hexStringToTextString("efbbbf68656c6c6f20776f726c6421e4bda0e5a5bd2100", Charset.forName("UTF-8")) //"hello world!你好!"
     *   //不含 BOM
     *   hexStringToTextString("68656c6c6f20776f726c6421e4bda0e5a5bd2100", Charset.forName("UTF-8")) //"hello world!你好!"
     *   hexStringToTextString("68656c6c6f20776f726c6421e4bda0e5a5bd21", Charset.forName("UTF-8")) //"hello world!你好!"
     * @note 核心原理: new String( textBytes, charset );
     * @param hexString
     * @return
     * @usage
     *  1. hexStringToTextString("4861") => "Ha"
     *      即 : 0x48 , 0x61 => 71 , 97 => 'H' , 'a'
     */
    public static String hexStringToTextString(String hexString, Charset charset) {
        if (hexString == null || hexString.equals("")) {
            return null;
        }
        hexString = hexString.replace(" ", "");

        //hex string to bytes
        byte[] bytes = new byte[hexString.length() / 2];
        for (int i = 0; i < bytes.length; i++) {
            try {
                bytes[i] = (byte) (0xff & Integer.parseInt(hexString.substring(i * 2, i * 2 + 2), 16));
            } catch (Exception e) {
                log.error("Fail to convert hex string to text string cause that parse substring to int or byte!hexString:{},index:{},exception:{}", hexString, i, e);//16进制转换成为string类型字符串失败
                //e.printStackTrace();
            }
        }
        String text = bytesToTextString(bytes, charset);
        return text;
    }

    public static String hexStringToTextString(String hexString) {
        return hexStringToTextString(hexString, null);
    }

    public static String bytesToTextString(byte [] textBytes, Charset charset) {
        String text = null;
        try {
            text = new String(textBytes, charset==null? CHARSET_DEFAULT:charset);
        } catch (Exception e) {
            log.error("Fail to convert text bytes to text string cause that byte to string!bytesHex:{},exception:{}", BytesUtils.bytesToHexString(textBytes) , e);
        }
        if(text != null){//去除 0x00 占位符(ASCII表中表示 NUT 符) | 在对二进制数组转 文本 的过程中,如果存在 0x00(在 ASCII 表中表示 NUT 字符),最终会生成乱码
            text = text.replaceAll(PLACEHOLDER_UTF8_0X00,"");
        }
        if(log.isDebugEnabled()){
            log.debug("textBytes : {}, charset:{}, text:`{}`", BytesUtils.bytesToHexArrayString(textBytes), charset, text);
        }
        return text;
    }

    /* ------------------------ 如下方法,待测验 TODO ---------------------- */

    /**
     * 从输入流中获取字节数组
     * @param in
     * @return
     */
    public static byte[] getBytes(InputStream in) {
        byte[] data = null;

        try {
            byte[] buffer = new byte[1024];

            int len;
            for (; (len = in.read(buffer)) > 0; data = addArray(data, buffer, len)) {
            }
        } catch (IOException var4) {
            log.error(var4.getMessage(), var4);
        }

        return data;
    }

    public static byte[] addArray(byte[] source, byte[] addedAry) {
        return addArray(source, addedAry, addedAry.length);
    }

    public static byte[] addArray(byte[] source, byte[] addedAry, int availableAddedAryLength) {
        if (addedAry == null) {
            return source;
        } else {
            byte[] buf = new byte[(source == null ? 0 : source.length) + availableAddedAryLength];
            int i;
            if (source != null) {
                for (i = 0; i < source.length; ++i) {
                    buf[i] = source[i];
                }
            }

            for (i = 0; i < availableAddedAryLength; ++i) {
                buf[(source == null ? 0 : source.length) + i] = addedAry[i];
            }

            return buf;
        }
    }

    public static int contains(byte[] source, byte[] data) {
        if (source == null || data == null) { return -1; }
        if (source.length >= data.length && data.length != 0) {
            int pos = -1;
            for (int i = 0; i <= source.length - data.length; ++i) {
                boolean flag = true;
                for (int j = 0; j < data.length; ++j) {
                    if (source[i + j] != data[j]) { flag = false; }
                }
                if (flag) { pos = i; break; }
            }
            return pos;
        }
        return -1;
    }

    public static boolean startWith(byte[] source, byte[] startData) {
        if (source == null) {
            return false;
        } else if (startData == null) {
            return false;
        } else if (source.length >= startData.length && startData.length != 0) {
            boolean flag = true;

            for (int i = 0; i < startData.length; ++i) {
                if (source[i] != startData[i]) {
                    flag = false;
                    break;
                }
            }

            return flag;
        } else {
            return false;
        }
    }

    public static boolean endWith(byte[] source, byte[] endData) {
        if (source == null) {
            return false;
        } else if (endData == null) {
            return false;
        } else if (source.length >= endData.length && endData.length != 0) {
            boolean flag = true;

            for (int i = 0; i < endData.length; ++i) {
                if (source[source.length - endData.length + i] != endData[i]) {
                    flag = false;
                    break;
                }
            }

            return flag;
        } else {
            return false;
        }
    }

    public static byte[] removeStart(byte[] source, int len) {
        if (source == null) {
            return null;
        } else if (source.length < len) {
            return null;
        } else {
            byte[] buf = new byte[source.length - len];

            for (int i = 0; i < buf.length; ++i) {
                buf[i] = source[i + len];
            }

            return buf;
        }
    }

    public static byte[] removeEnd(byte[] source, int len) {
        if (source == null) {
            return null;
        } else if (source.length < len) {
            return null;
        } else {
            byte[] buf = new byte[source.length - len];

            for (int i = 0; i < buf.length; ++i) {
                buf[i] = source[i];
            }

            return buf;
        }
    }

    /**
     * 字节数组裁剪为目标子数组
     * @note
     *     BytesUtils.subByteArray( new byte [] {0x01, 0x02, 0x03, 0x04}, 0, 3) = byte [] {0x01, 0x02, 0x03, 0x04}
     * @param data
     * @param beginIndex
     * @param endIndex
     * @return
     */
    public static byte[] subByteArray(byte[] data, int beginIndex, int endIndex) {
        if (beginIndex < 0) {
            throw new IndexOutOfBoundsException("beginIndex must bigger than 0,but it is " + beginIndex + "");
        } else if (endIndex >= data.length) {
            throw new IndexOutOfBoundsException("endIndex must smaller than data lenght " + data.length + ",but it is " + endIndex + "");
        } else {
            byte[] buf = new byte[endIndex - beginIndex + 1];

            for (int i = beginIndex; i <= endIndex; ++i) {
                buf[i - beginIndex] = data[i];
            }

            return buf;
        }
    }

    public static byte[] remove(byte[] data, int start, int size) {
        if (data == null) {
            return null;
        } else if (start >= data.length) {
            throw new IndexOutOfBoundsException("start must smaller than data's length:'" + data.length + "',but it is " + start + "");
        } else if (start + size > data.length) {
            throw new IndexOutOfBoundsException("start+size must smaller than data's length:'" + data.length + "',but they are " + start + "+" + size + "");
        } else {
            byte[] buf = new byte[data.length - size];

            int i;
            for (i = 0; i < start; ++i) {
                buf[i] = data[i];
            }

            for (i = start + size; i < data.length; ++i) {
                buf[i - size] = data[i];
            }

            return buf;
        }
    }

    public static byte[] removeContains(byte[] source, byte[] data) {
        int pos;
        if (source == null) {
            return null;
        } else if (data == null) {
            return source;
        } else {
            if ((pos = contains(source, data)) == -1) {
                return source;
            } else if (source.length < data.length) {
                return null;
            } else {
                byte[] buf = new byte[source.length - data.length];

                int i;
                for (i = 0; i < pos; ++i) {
                    buf[i] = source[i];
                }

                for (i = pos + data.length; i < source.length; ++i) {
                    buf[i - data.length] = source[i];
                }

                return buf;
            }
        }
    }

    /** 打印二进制字符串 (每8bit,打印为1行)
     * @param bitString 字符串化的二进制字节流
     *  note : 长度必须为8的倍数(因: 1 byte = 8 bit)
     *  sample : "1011011110100011000011110000000000000000000000000000000000000000"
     * @param isEnable 是否启用本方法的日志打印
     **/
    public static String printBitString(String bitString, boolean isEnable){
        if( bitString == null || bitString.length() %8!=0){//长度必须是8的整数倍
            throw new RuntimeException("The length of input bit string must be an integer multiple of 8! bit string : " + bitString);
        }
        if(bitString == ""){//bitString == ""
            return "";
        }
        StringBuilder result = new StringBuilder();
        String bit8 = null;
        while (bitString.length() >= 8) {
            bit8 = bitString.substring(0, 8);//第 (0, 8] 位 ,共计 8 位
            result.append( String.format("                %s        : %s\n", bit8, BytesUtils.binaryStringToHexString(bit8)) );// 01111111 : 7F
            bitString = bitString.substring(8); //第 (8 , +∞) 位,递归
        }
        return result.toString();
    }

    public static String printBitString(String bitString){
        return printBitString(bitString, true);
    }

    /** 打印十六进制字符串 (每个字节,为1个打印单元)
     * @param hexString 字符串化的十六进制字节流
     *  note : 长度必须为2的倍数(因: 1 byte = 2 个 十六进制字符串. 如: 0x7A = 122)
     *  sample : "7A7A7A"
     * @param byteSplitChar 字节之间的分隔符 (默认: " ")
     * @param isEnable 是否启用本方法的日志打印
     * @return "7A 7A 7A"
     **/
    public static String printHexString(String hexString, String byteSplitChar, boolean isEnable){
        if( hexString == null || hexString.length() %2 != 0){//长度必须是2的整数倍
            throw new RuntimeException("The length of input hex string must be an integer multiple of 2! hex string : " + hexString);
        }
        if(hexString == ""){//hexString == ""
            return "";
        }
        byteSplitChar = (byteSplitChar == null || byteSplitChar.equals("")) ? " " : byteSplitChar;
        StringBuilder result = new StringBuilder();
        String byteHex = null;
        while (hexString.length() >= 2) {
            byteHex = hexString.substring(0, 2);//第 (0, 2] 位 ,共计 2 位
            result.append( String.format("%s%s", byteHex, byteSplitChar) );// 7F : 7F
            hexString = hexString.substring(2); //第 (2 , +∞) 位,递归
        }
        return result.substring(0, result.length()- byteSplitChar.length());//去除末尾处多余的分隔符
    }

    public static String printHexString(String hexString){
        return printHexString(hexString, " ", true);
    }

    /**
     * Int 转 byte 数组
     * @note
     * 1. 原理:将 int 数据中的四个byte取出,分别存储
     * 即:
     *   byte[] bytes = new byte[4];
     *   bytes[0] = (byte) (value & 0xff);// 最低位
     *   bytes[1] = (byte) ((value >> 8) & 0xff);// 次低位
     *   bytes[2] = (byte) ((value >> 16) & 0xff);// 次高位
     *   bytes[3] = (byte) (value >>> 24);// 最高位,无符号右移
     * 2. 辅助分析工具: {@link BytesUtils # intToHexString(xxxx) / hexStringToBinaryString(96) }
     * 3. Java byte 取值范围是 [-128, +127]. Java中的 byte 数据类型是 8 位、有符号的,其取值范围是从 -128 到 127
     *   3.1 在计算机内存存放的数值都是【补码】形式,第1位为符号位
     *   3.2 在补码中,正数的补码与其原码相同
     *   3.3 负数的补码是将其原码的每位取反后,再加1
     * @sample
     * 1. value=123, return=bytes[0, 0, 0, 123] = 0x00 00 00 7B = 0111 1011(BIN)
     * 2. value=5674, return=bytes[0, 0, 22, 42] = 0x00 00 16 2A = 0001 0110 0010 1010(BIN)
     * 3. value=9 999 999, return=bytes[0, -104, -106, 127] = 0x00 98 96 7F = 1001 1000 1001 0110 0111 1111(BIN)
     *    bytesToHexString( intToBytes(9999999) ) = "0098967f"
     *     正整数 转 byte : 以整数 123 为例  => 0111 1011(BIN) = 0x7B
     *     负整数 转 byte : 以整数 -104 为例 => 1001 1000(BIN) = 0x98
     *       正整数 104 的原码 = 0110 1000
     *       每位取反         = 1001 0111
     *       再+1            = 1001 1000(BIN)
     * @reference-doc
     * [1] java基本数据类型byte的取值范围-128~127,以及溢出后取值的实现 - CSDN - https://blog.csdn.net/luzhensmart/article/details/107203110
     * [2] 计算机编码方式:原码、反码、补码 - 博客园 - 18132824
     * [3] java中byte数组与int类型的转换(两种方式) - CSDN - https://blog.csdn.net/z69183787/article/details/38564219
     * @param value
     * @return
     */
    public static byte[] intToBytes(int value) {
        byte[] bytes = new byte[4];
        for (int i = 0; i < 4; i++) {
            bytes[i] = (byte) (value >> (24 - i * 8));
        }
        return bytes;
    }

    /**
     * long类型转byte[]
     * @usage
     *     byte [] bytes = longToBytes(2418704398L) = byte [] { 0,0,0,0, -112,42,-128,14 }
     *     BytesUtils.bytesToHexString( bytes ) = "00000000902a800e"
     * @param l
     * @return
     */
    public static byte[] longToBytes(long l){
        //分配缓冲区,单位为字节,一个long类型占8个字节,所以分配为8
        ByteBuffer byteBuffer = ByteBuffer.allocate(Long.SIZE/Byte.SIZE);
        //参数一为起始位置(不指定默认为0),参数二为所放的值
//        byteBuffer.putLong(0,l);
        byteBuffer.putLong(0,l);
        return byteBuffer.array();
    }


    /**
     * bytes 转 int
     * 1个 byte 数据左移24位变成0x??000000,再右移8位变成0x00??0000
     * @note java 中 int 占 4 个字节
     * @sample
     * 1. bytes = [0, 0, 0, 123], return : 123
     * @reference-doc
     * [1] java byte数组和int互转 - Zhihu - https://zhuanlan.zhihu.com/p/580220308
     * @param bytes (必须是 4 个字节,否则报错)
     * @return
     */
    public static int bytesToInt(byte [] bytes) {
        int intByteSize = 4;//Int 的字节数
        int value = 0;
        for (int i = 0; i < intByteSize; i++) {
            value <<= 8;// < < = 8
            int b = bytes[i] & 0xFF; //
            value |= b;// | = b
        }
        return value;
    }

    /**
     * 字节数据转 Long
     * @note java 中 long 占 8 字节
     * @sample 0x00 00 01 97 ae e7 86 17 => 1750986098199
     * @param bytes (必须是 8 个字节,否则报错)
     * @return
     */
    public static long bytesToLong(byte[] bytes) {
        int longByteSize = 8;
        long value = 0;
        for (int i = 0; i < longByteSize; i++) {
            value <<= 8;
            int b = bytes[i] & 0xFF;
            value |= b;
        }
        return value;
    }

    /**
     * 浮点数转 byte 数组
     * @note
     * 1. 依据1: Java Float 共计 4 字节,32 bit
     * 2. 原理: 将 float 数据中的 4个 byte 取出,分别存储
     * @sample
     * 1. value=1.23f , return
     *     = 0x3f 9d 70 a4 = bytes[63, -99 , 112, -92] = bytes[63, 157, 112, 164] (x)
     *     = 0011 1111 1001 1101 0111 0000 1010 0100(BIN)
     *     = 1067282596(DEC)
     *   补充: bytesToHexString(new byte[] {63, -99 , 112, -92}) = "3f9d70a4"
     * @param value
     * @reference-doc
     * [1]
     * @return
     */
    public static byte[] floatToBytes(float value) {//eg: 1.23f
        //1:float 转 byte 数组
        int bits = Float.floatToIntBits(value);//eg: 1067282596(DEC)
        byte[] bytes = new byte[4];
        for (int i = 0; i < 4; i++) {
            bytes[i] = (byte) ((bits >> (24 - (8 * i))) & 0xFF);
        }

        //2:字节序(byte order)与大小端的处理 : 略
//        // 翻转数组
//        int len = bytes.length;
//        // 建立一个与源数组元素类型相同的数组
//        byte[] dest = new byte[len];
//        // 为了防止修改源数组,将源数组拷贝一份副本
//        System.arraycopy(bytes, 0, dest, 0, len);
//        byte temp;
//        // 将顺位第i个与倒数第i个交换
//        for (int i = 0; i < len / 2; ++i) {
//            temp = dest[i];
//            dest[i] = dest[len - i - 1];
//            dest[len - i - 1] = temp;
//        }

        return bytes;//eg: 0x3f9d70a4
    }

    /**
     * bytes 转 Float
     * @note
     * 1. Java Float : 在Java中,float类型是单精度浮点数,遵循IEEE 754标准。
     *   它占用4个字节(32位),由符号位、指数位和尾数位组成:
     *   符号位(S):1 bit,表示正负,0为正,1为负。
     *   指数位(E):8 bit,表示指数部分,范围为-126到127。
     *     23-30位, 共 8 bit为指数位,这里指数的底数规定为 2 (取值范围:0-255)
     *     这一部分的最终结果格式为: 2^{E-127}
     *   尾数位(M):23 bit,表示小数部分。
     * 2. float 的精度由尾数位决定,共23位。其有效小数位为6到7位,能保证6位为绝对精确,7位一般也是正确的,8位就不一定了
     * @reference-doc
     * [1] Java中float/double取值范围与精度 - CSDN - https://blog.csdn.net/a327369238/article/details/52354811
     * @sample
     * 1. bytes={63, -99 , 112, -92}
     *     = 0x3f 9d 70 a4 = bytes[63, 157, 112, 164] (x)
     *     = 0011 1111 1001 1101 0111 0000 1010 0100(BIN)
     *     = 1067282596(DEC)
     *    return = 1.23f
     *   补充: bytesToHexString(new byte[] {63, -99 , 112, -92}) = "3f9d70a4"
     * @param bytes
     * @return
     */
    public static float bytesToFloat(byte[] bytes) {
        int bits = 0;
        for (int i = 0; i < 4; i++) {
            bits |= (bytes[i] & 0xFF) << (24 - (8 * i));
        }
        return Float.intBitsToFloat(bits);
    }

    /**
     * Double 转 bytes
     * @sample
     * 1. value=123.56
     *    return = [-92, 112, 61, 10, -41, -29, 94, 64]
     *           = 0xa4703d0ad7e35e40
     * @param value
     * @return
     */
    public static byte[] doubleToBytes(double value) {
        long longBits = Double.doubleToRawLongBits(value);
        byte[] byteRet = new byte[8];
        for (int i = 0; i < 8; i++) {
            byteRet[i] = (byte) ((longBits >> 8 * i) & 0xff);
        }
        return byteRet;
    }

    /**
     * bytes 转 Double
     * @sample
     * 1. bytes=[-92, 112, 61, 10, -41, -29, 94, 64] = 0xa4703d0ad7e35e40
     *    return = 123.56
     * @param bytes
     * @return
     */
    public static double bytesToDouble(byte[] bytes) {
        long value = 0;
        for (int i = 0; i < 8; i++) {
            value |= ((long) (bytes[i] & 0xff)) << (8 * i);
        }
        return Double.longBitsToDouble(value);
    }

    private static int getUnsignedShort(short value) {
        return value & 0xFFFF;
    }

    /**
     * 获取机器的字节序
     * @return
     */
    public static ByteOrder getByteOrder(){
        ByteOrder byteOrder = ByteOrder.nativeOrder();
        return byteOrder;
    }

    /**
     * 大端转小端
     * @param bigEndianBytes
     * @return
     */
    public static byte[] bigEndianToLittleEndian(byte[] bigEndianBytes) {
        ByteBuffer byteBuffer = ByteBuffer.wrap(bigEndianBytes);
        byteBuffer.order(ByteOrder.BIG_ENDIAN); // 设置为大端字节序
        int bigEndianValue = byteBuffer.getInt(); // 获取大端表示的整数

        ByteBuffer littleEndianBuffer = ByteBuffer.allocate(4);
        littleEndianBuffer.order(ByteOrder.LITTLE_ENDIAN); // 设置为小端字节序
        littleEndianBuffer.putInt(bigEndianValue); // 将整数以小端字节序写入新缓冲区
        int littleEndianValue = littleEndianBuffer.getInt();

        byte[] littleEndianBytes = littleEndianBuffer.array();

        if(log.isDebugEnabled()){
            log.debug("bigEndianBytes:{}, bigEndianValue:{}; littleEndianBytes:{}, littleEndianBuffer:{}", bytesToHexString(bigEndianBytes), bytesToHexString(littleEndianBytes), bigEndianValue, littleEndianValue);
        }
        return littleEndianBytes;// 返回小端字节数组
    }

    /**
     * 小端转大端
     * @param littleEndianBytes
     * @return
     */
    public static byte[] littleEndianToBigEndian(byte[] littleEndianBytes) {
        ByteBuffer byteBuffer = ByteBuffer.wrap(littleEndianBytes);
        byteBuffer.order(ByteOrder.LITTLE_ENDIAN); // 设置为小端字节序
        int value = byteBuffer.getInt(); // 获取小端表示的整数
        ByteBuffer bigEndianBuffer = ByteBuffer.allocate(4);
        bigEndianBuffer.order(ByteOrder.BIG_ENDIAN); // 设置为大端字节序
        bigEndianBuffer.putInt(value); // 将整数以大端字节序写入新缓冲区
        return bigEndianBuffer.array(); // 返回大端字节数组
    }

    //dbc.DbcReader#bigEndianLeastSignificantBitOffset

    /**
     * 计算大端字节序的最低有效位偏移量
     * Calculates the least significant bit offset for big endian byte order
     *
     * @reference-doc {@link DbcReader#bigEndianLeastSignificantBitOffset }
     *
     * @param msb    Most significant bit offset
     * @param length signal length in bit
     * @return lsb Least significant bit offset
     */
    public static int bigEndianLeastSignificantBitOffset(int msb, int length) {
        int lsb;
        int pos;
        int cpos;
        int bytes;
        pos = 7 - (msb % 8) + (length - 1);
        if (pos < 8) {
            /* msb pass a byte order */
            lsb = msb - length + 1;
        } else {
            cpos = 7 - (pos % 8);
            bytes = pos / 8;
            lsb = cpos + (bytes * 8) + (msb / 8) * 8;
        }
        return lsb;
    }

    /**
     * 获取大端模式中预处理二进制序列的起始比特位
     * @note 主要用途 大端字节序(摩托罗拉格式)下获取预处理的二进制序列的起始比特位置
     * @param binarySequenceBitsSize 二进制序列的总比特位数
     *  如: CAN帧的总Bit数
     * @param bigEndianLeastSignificantBitOffset 大端模式下的偏移量
     *  以 CAN总线的DBC文件为例,此处的值不是dbc的 origin start bit,而是基于 origin start bit 和字节序 计算出的 start bit
     * @return
     */
    public static int getBigEndianPreHandleBinarySequenceStartBit(int binarySequenceBitsSize, int bigEndianLeastSignificantBitOffset) {
        int originStartBit = bigEndianLeastSignificantBitOffset;
        //总共有多少字节
        int totalBytesSize = binarySequenceBitsSize / 8;
        //原始位前有多少字节
        int x = originStartBit / 8;
        //原始位后有多少字节
        int y = totalBytesSize - 1 - x;
        return originStartBit + y * 8 - x * 8;
    }

    /**
     * 按照指定单位长度,对序列数据进行逆序处理
     * @note 主要用途: 当 字节数组数据为小端字节序(intel模式)时,遍历每个字节,对每个字节的二进制bit序列做逆序反转
     *
     * @param sequence 序列串
     *   eg: "0000000001010000110101000100000010000001000000100000010100001010"
     *   即: 00000000 01010000 11010100 01000000 10000001 00000010 00000101 00001010
     * @param unitLength 逆序单元的长度 eg: 8
     * @return
     *   eg: ""
     */
    public static String reverse(String sequence, Integer unitLength) {
        unitLength = unitLength==null? 8 : unitLength;//默认:单位长度: 8 bit
        StringBuilder sb = new StringBuilder();
        int unitsSize = sequence.length() / unitLength;//单元数
        if (sequence.length() % unitLength != 0) {
            if( log.isDebugEnabled() ) {
                log.debug("Fail to reverse!sequence:{},unitLength:{}", sequence, unitLength);
            }
            return null;
        }

        for (int i = 0; i < unitsSize; i++) {
            sb.append(new StringBuilder(sequence.substring(i * unitLength, i * unitLength + unitLength)).reverse());
        }

        return sb.toString();
    }

    /**
     * 因大小端问题,对二进制序列做预处理
     * @param binarySequence
     * @param byteOrder 字节序
     * @return 预处理完成的二进制序列
     */
    public static String binarySequencesEndianPreHandle(String binarySequence, ByteOrder byteOrder){
        if(binarySequence == null || "".equals(binarySequence) || byteOrder == null){
            throw new RuntimeException( String.format("Fail to pre handle!binarySequence:`%s`, byteOrder:%s", binarySequence, byteOrder) );
        }
        String binarySequenceResult = null;
        if( ByteOrder.LITTLE_ENDIAN.equals(byteOrder) ) {//小端字节序: 每个字节分别按 8bit 逆序,但字节之间的顺序不变
            binarySequenceResult = reverse(binarySequence, 8);//8: 1个字节的bit个数为 8 bit
        } else if(ByteOrder.BIG_ENDIAN.equals(byteOrder)){//大端字节序: 每个字节分别按 8bit 逆序;字节之间,也逆序一次
            binarySequenceResult = (new StringBuilder(binarySequence)).reverse().toString();//或 reverse(binarySequence, binarySequence.length())
        }
        return binarySequenceResult;
    }

    /**
     * 16进制序列大小端预处理
     * @param hexSequence
     * @param byteOrder
     * @return 预处理完成的二进制序列
     */
    public static String hexSequencesEndianPreHandle(String hexSequence, ByteOrder byteOrder){
        String binarySequence = hexStringToBinaryString(hexSequence);
        return binarySequencesEndianPreHandle(binarySequence, byteOrder);
    }

    /**
     * 从已预处理的二进制序列中抽取预处理序列中的比特序列
     * @note 针对原始的字节数据,需先做前置步骤 ( {@link #binarySequencesEndianPreHandle } )后,方可调用此方法
     * @param preHandledBinarySequence 预处理好的二进制序列
     * @param byteOrder 字节序
     * @param originStartBit 含字节序意义的起始比特位
     *     例如: DBC 文件中定义的原始的 StartBit
     * @param bitLength 位长
     * @return 预处理好的比特序列
     */
    public static String extractPreHandleBitsFromPreHandledBinarySequence(
        String preHandledBinarySequence, ByteOrder byteOrder
        , Integer originStartBit, Integer bitLength
    ){
        String preHandledBits = null;
        Integer preHandledBinarySequenceStartBit = getPreHandleBinarySequenceStartBit(preHandledBinarySequence.length(), originStartBit, bitLength, byteOrder);
        if(ByteOrder.LITTLE_ENDIAN.equals(byteOrder)){
            //preHandledBinarySequenceStartBit = startBit;
            preHandledBits = preHandledBinarySequence.substring(preHandledBinarySequenceStartBit, preHandledBinarySequenceStartBit + bitLength);
        } else if( ByteOrder.BIG_ENDIAN.equals( byteOrder ) ) {
            //int bigEndianLeastSignificantBitOffset = bigEndianLeastSignificantBitOffset( originStartBit, bitLength );//计算大端字节序的最低有效位偏移量
            //preHandledBinarySequenceStartBit = getBigEndianPreHandleBinarySequenceStartBit(preHandledBinarySequence.length(), bigEndianLeastSignificantBitOffset);//获取大端模式中预处理二进制序列的起始比特位
            preHandledBits = preHandledBinarySequence.substring( preHandledBinarySequenceStartBit, preHandledBinarySequenceStartBit + bitLength );
        }
        return preHandledBits;
    }

    /**
     * 获取在总Bit序列中的最终起始位置
     * @usage int finalStartBit = getSignalStartBit(bitSize, startBit, byteOrder);
     * @param bitSize 总Bit序列的bit数
     *   例如:CAN帧的总bit数
     * @param originStartBit
     *   例如: 是 DBC 中定义的 originStartBit,而非 DbcReader 根据 origin start bit + 字节序,计算出的 startBit 值
     * @param targetSubBitLength 目标子bit序列的位长 (大端序列时,必须传入)
     * @param byteOrder 字节序
     * @return
     */
    public static int getPreHandleBinarySequenceStartBit(int bitSize, int originStartBit,int targetSubBitLength, ByteOrder byteOrder) {
        int finalStartBit = -1;
        if( ByteOrder.BIG_ENDIAN.equals( byteOrder ) ) {
            //return getSignalStartBitForBigEndian(bitSize, startBit);
            int bigEndianLeastSignificantBitOffset = bigEndianLeastSignificantBitOffset( originStartBit, targetSubBitLength );//计算大端字节序的最低有效位偏移量
            finalStartBit = BytesUtils.getBigEndianPreHandleBinarySequenceStartBit( bitSize, bigEndianLeastSignificantBitOffset );
        } else if( ByteOrder.LITTLE_ENDIAN.equals( byteOrder ) ){
            finalStartBit = originStartBit;
        } else {
            throw new RuntimeException("Not support!originStartBit:" + originStartBit + ", finalStartBit:" + finalStartBit + ", targetSubBitLength:" + targetSubBitLength + ", bitSize:" + bitSize + ", byteOrder:" + byteOrder);
        }

        if(log.isDebugEnabled()){
            log.debug(
                "originStartBit:" + originStartBit + ", finalStartBit:" + finalStartBit + ", targetSubBitLength:" + targetSubBitLength + ", bitSize:" + bitSize + ", byteOrder:" + byteOrder
            );
        }
        return finalStartBit;
    }

    /**
     * 从原始二进制序列中抽取预处理序列中的比特序列,并完成最终转换
     */
    public static String extractTargetBitsFromPreHandledBinarySequence(
        String preHandledBinarySequence, ByteOrder byteOrder
        , Integer startBit, Integer bitLength
    ){
        String preHandledBits = extractPreHandleBitsFromPreHandledBinarySequence(
             preHandledBinarySequence, byteOrder, startBit, bitLength
        );
        String reversedBits = (new StringBuilder( preHandledBits )).reverse().toString();//还原后的目标比特位
        return reversedBits;
    }

    /**
     * 从十六进制序列中抽取目标二进制比特序列
     * @note 从16进制序列中按照指定的大小端(字节序),提取指定长度的、指定偏移位的二进制串
     * @param hexSequence 16进制的字符串序列
     * @param byteOrder
     * @param startBit
     * @param bitLength
     * @return 目标二进制比特序列
     */
    public static String extractTargetBits(
        String hexSequence, ByteOrder byteOrder
        , Integer startBit, Integer bitLength
    ){
        String preHandledBinarySequence = hexSequencesEndianPreHandle(hexSequence, byteOrder);//16进制字符串序列 转 预处理的二进制序列
        String targetBits = extractTargetBitsFromPreHandledBinarySequence(
            preHandledBinarySequence, byteOrder, startBit, bitLength
        );
        return targetBits;
    }

    public static String extractTargetBitsByBinarySequence(
        String binarySequence, ByteOrder byteOrder
        , Integer startBit, Integer bitLength
    ){
        String preHandledBinarySequence = binarySequencesEndianPreHandle(binarySequence, byteOrder);//2进制字符串序列 转 预处理的二进制序列
        String targetBits = extractTargetBitsFromPreHandledBinarySequence(
            preHandledBinarySequence, byteOrder, startBit, bitLength
        );
        return targetBits;
    }

    /**
     * 获取 Unicode 文本的字符集
     * @param textBytes
     * @return
     */
    public static Charset getUnicodeTextCharset(byte[] textBytes){
        String encoding = null;
        int bomSize = 4;//BOM_SIZE;
        byte bom[] = new byte[bomSize];
        int n, unread;
        //n = internalIn.read(bom, 0, bom.length);

        //读取 bom
        int off = 0;
        int len = bom.length;
        int pos = 0;

        if (bom == null) {
            throw new NullPointerException();
        } else if (off < 0 || len < 0 || len > bom.length - off) {
            throw new IndexOutOfBoundsException();
        }
        int avail = bom.length <= textBytes.length ? bom.length : textBytes.length ;//算 bom.length 与 textBytes.length 的最小值
        if (avail > 0) {
            System.arraycopy(textBytes, pos, bom, off, avail);
        }

        //判断 unicode 字符集
        if ((bom[0] == (byte) 0x00) && (bom[1] == (byte) 0x00)
                && (bom[2] == (byte) 0xFE) && (bom[3] == (byte) 0xFF)) {
            encoding = "UTF-32BE";
            //unread = n - 4;
        } else if ((bom[0] == (byte) 0xFF) && (bom[1] == (byte) 0xFE)
                && (bom[2] == (byte) 0x00) && (bom[3] == (byte) 0x00)) {
            encoding = "UTF-32LE";
            //unread = n - 4;
        } else if ((bom[0] == (byte) 0xEF) && (bom[1] == (byte) 0xBB)
                && (bom[2] == (byte) 0xBF)) {
            encoding = "UTF-8";//utf08 with bom
            //unread = n - 3;
        } else if ((bom[0] == (byte) 0xFE) && (bom[1] == (byte) 0xFF)) {
            encoding = "UTF-16BE";
            //unread = n - 2;
        } else if ((bom[0] == (byte) 0xFF) && (bom[1] == (byte) 0xFE)) {
            encoding = "UTF-16LE";
            //unread = n - 2;
        } else {
            // Unicode BOM mark not found, unread all bytes
            //defaultEncoding = defaultEncoding == null ? Charset.defaultCharset().name() : defaultEncoding;
            //defaultEncoding = defaultEncoding == null ? null : defaultEncoding;
            //encoding = defaultEncoding;
            //unread = n;
            encoding = "UTF-8";//默认: UTF-8 (without bom)
        }
        // System.out.println("read=" + n + ", unread=" + unread);
        return Charset.forName(encoding);
    }


    /**
     * 从前往后删除指定的字符,直至下一个字符不为目标字符为止
     * @sample
     *   1. removePrefixChar('0', "0d") = "d"
     *   2. removePrefixChar('0', "000d32231313") = "d32231313"
     * @return
     */
    public static String removePrefixChar(char targetChar, CharSequence charSequence){
        int length = charSequence.length();
        StringBuilder result = new StringBuilder();
        for (int i = 0; i < length; i++) {
            char currentChar = charSequence.charAt( i );
            if( targetChar == currentChar){//一旦等于,就移除当前字符

            } else {
                result.append( charSequence.subSequence(i, length) );
                break;//一旦存在非目标字符时,立即停止
            }
        }
        return result.toString();
    }



    public static void main(String[] args) {
        //clearHexStringPrefixNullCharacters("004856");
        byte [] bytes = floatToBytes(1.23f);
        log.info("bytes: 0x{}", bytesToHexString(bytes) );//bytes: 0x3f9d70a4 = 1067282596(DEC)

        Float floatValue = bytesToFloat( bytes );
        log.info("floatValue:{}", floatValue);//1.23
    }

}

BytesUtilsTest


import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;

import java.nio.ByteOrder;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;

/**
 * 字节操作工具的测试类
 * @update-time 2025/06/10 16:40
 */

@Slf4j
public class BytesUtilsTest {
    @Test
    public void bytesToIntTest(){
        int intValue = 123;
        byte [] bytes = BytesUtils.intToBytes( intValue );
        //bytes:[0, 0, 0, 123], hex:0000007b
        log.info("bytes:{}, hex:{}", bytes, BytesUtils.bytesToHexString(bytes));

        log.info("intValue:{}", BytesUtils.bytesToInt(bytes) );//123
    }

    @Test
    public void bytesToDoubleTest(){
        Double doubleValue = 123.56d;
        byte [] bytes = BytesUtils.doubleToBytes( doubleValue );
        //[-92, 112, 61, 10, -41, -29, 94, 64], hex:a4703d0ad7e35e40
        log.info("bytes:{}, hex:{}", bytes, BytesUtils.bytesToHexString(bytes));

        log.info("doubleValue:{}", BytesUtils.bytesToDouble(bytes) );//123.56
    }

    @Test
    public void bytesToStringTest(){
        String stringValue = "Hello World!";
        Charset charset = StandardCharsets.UTF_8;
        byte [] bytes = BytesUtils.stringToBytes( stringValue, charset );
        String bytesHex = BytesUtils.bytesToHexString(bytes);
        //[72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33] , hex: 48656c6c6f20576f726c6421
        log.info("bytes:{}, hex:{}", bytes, bytesHex);

        log.info("stringValue:{}", BytesUtils.bytesToString(bytes, charset) );//Hello World!
        log.info("stringValue2:{}", BytesUtils.hexStringToTextString( bytesHex, charset) );//Hello World!
    }

    @Test
    public void getByteOrderTest(){//获取当前机器的大小端
        System.out.println( BytesUtils.getByteOrder() );//LITTLE_ENDIAN
    }

    /**
     * 从16进制序列中按照指定的大小端(字节序),提取指定长度的、指定偏移位的二进制串
     * @note 本示例中的样例序列对应的结果,均为正确结果
     */
    @Test
    public void extractTargetBitsTest(){
        String bits = null;

        //CAN帧 Payload
        bits = BytesUtils.extractTargetBits("0050D4408102050A", ByteOrder.LITTLE_ENDIAN, 17, 9);//'001101010'
        log.info("bits:{}", bits);

        bits = BytesUtils.extractTargetBits("7f7f010110110021", ByteOrder.BIG_ENDIAN, 22, 7);// '0000001'
        log.info("bits:{}", bits);

        bits = BytesUtils.extractTargetBits("7f7f010110110021", ByteOrder.BIG_ENDIAN, 22, 7);// '0000001'
        log.info("bits:{}", bits);

        //SOME/IP 报文 Payload
        bits = BytesUtils.extractTargetBits("ff", ByteOrder.BIG_ENDIAN, 71*8-1, 8);// '11111111'
        log.info("bits:{}", bits);
    }
}

Y 推荐文献

含:HexStringReader(原: SourceStringReader)

X 参考文献

posted @ 2024-07-10 09:37  千千寰宇  阅读(926)  评论(0)    收藏  举报