[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 参考文献
本文作者:
千千寰宇
本文链接: https://www.cnblogs.com/johnnyzen
关于博文:评论和私信会在第一时间回复,或直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
日常交流:大数据与软件开发-QQ交流群: 774386015 【入群二维码】参见左下角。您的支持、鼓励是博主技术写作的重要动力!
本文链接: https://www.cnblogs.com/johnnyzen
关于博文:评论和私信会在第一时间回复,或直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
日常交流:大数据与软件开发-QQ交流群: 774386015 【入群二维码】参见左下角。您的支持、鼓励是博主技术写作的重要动力!

浙公网安备 33010602011771号