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.xxxx.utils.bytes;
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.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.xml.bind.DatatypeConverter;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
/**
* 字节操作工具类
# @description 与嵌入式软件数据交互过程中,必然涉及各种的、大量的字节操作场景。如:16进制与10进制、2进制间的转换,字符串、byte数组与int之间的转换等,故此有此核心工具类的沉淀。
* @version v1.0
* @create-time 2022/12/23 18:18
* @description ...
*/
@Slf4j
public class BytesUtils {
private static final Logger logger = LoggerFactory.getLogger(BytesUtils.class);
/**
* 单个字节的位长
* 1byte = 8 bit
* 1个16进制位 占 4 bit => 1个字节可代表 2 个 16进制位, 1 个16进制位 = 0.5 个字节
**/
public final static int BYTE_BIT_LENGTH = 8;
/**
* 字节转二进制字符串
* @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 = com.seres.bigdata.poc.utils.StringUtils.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
* bytesToString(new byte[] { 0x48, 0x65 }) = "4865"
* @param byteArray
* @return
*/
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);
}
/**
* (多字节的)二进制字符串转二进制数组
* @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进制
*/
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 intToHexString(255) => "ff"
*/
public static String intToHexString(int v) {
String hexStr = Integer.toHexString(v);
if (hexStr.length() % 2 != 0) {
hexStr = "0" + hexStr;
}
return hexStr;
}
/**
* 16进制转换成为文本字符串
* @param hexString
* @return
* @usage
* 1. hexStringToTextString("4861") => "Ha"
* 即 : 0x48 , 0x61 => 71 , 97 => 'H' , 'a'
*/
public static String hexStringToTextString(String hexString) {
if (hexString == null || hexString.equals("")) {
return null;
}
hexString = hexString.replace(" ", "");
byte[] baKeyword = new byte[hexString.length() / 2];
for (int i = 0; i < baKeyword.length; i++) {
try {
baKeyword[i] = (byte) (0xff & Integer.parseInt(hexString.substring(i * 2, i * 2 + 2), 16));
} catch (Exception e) {
logger.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();
}
}
try {
hexString = new String(baKeyword, StandardCharsets.UTF_8);
} catch (Exception e) {
logger.error("Fail to convert hex string to text string cause that byte to string!hexString:{},exception:{}", hexString, e);//16进制转换成为string类型字符串失败
}
return hexString;
}
/* ------------------------ 如下方法,待测验 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) {
logger.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;
}
}
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();
}
}
标签:return,int,source,length,SE,new,Java,byte,ByteUtils
From: https://www.cnblogs.com/johnnyzen/p/18293230