package publicway;
|
|
import java.io.UnsupportedEncodingException;
|
import java.util.Arrays;
|
|
public class HexUtil {
|
|
private static final String HEX_CHARS = "0123456789ABCDEF";
|
|
/**
|
* 将字节数组转换为十六进制字符串
|
*
|
* @param src 字节数组
|
* @return 十六进制字符串,如果输入为空则返回null
|
*/
|
public static String bytesToHexString(byte[] src) {
|
return bytesToHexString(src, 0, src != null ? src.length : 0);
|
}
|
|
/**
|
* 将字节数组转换为十六进制字符串
|
*
|
* @param src 字节数组
|
* @param len 要转换的长度
|
* @return 十六进制字符串,如果输入为空则返回null
|
*/
|
public static String bytesToHexString(byte[] src, int len) {
|
return bytesToHexString(src, 0, len);
|
}
|
|
/**
|
* 将字节数组指定范围转换为十六进制字符串
|
*
|
* @param src 字节数组
|
* @param start 起始位置
|
* @param end 结束位置
|
* @return 十六进制字符串,如果输入为空则返回null
|
*/
|
public static String bytesToHexString(byte[] src, int start, int end) {
|
if (src == null || src.length == 0) {
|
return null;
|
}
|
|
StringBuilder stringBuilder = new StringBuilder();
|
for (int i = start; i < end && i < src.length; i++) {
|
int v = src[i] & 0xFF;
|
stringBuilder.append(HEX_CHARS.charAt(v >>> 4));
|
stringBuilder.append(HEX_CHARS.charAt(v & 0x0F));
|
}
|
return stringBuilder.toString();
|
}
|
|
/**
|
* 将十六进制字符串转换为字节数组
|
*
|
* @param hexString 十六进制字符串
|
* @return 字节数组,如果输入为空则返回null
|
*/
|
public static byte[] hexStringToBytes(String hexString) {
|
if (hexString == null || hexString.isEmpty()) {
|
return null;
|
}
|
|
String normalizedHex = hexString.toUpperCase().replaceAll("[^0-9A-F]", "");
|
if (normalizedHex.length() % 2 != 0) {
|
throw new IllegalArgumentException("十六进制字符串长度必须为偶数");
|
}
|
|
int length = normalizedHex.length() / 2;
|
byte[] result = new byte[length];
|
|
for (int i = 0; i < length; i++) {
|
int pos = i * 2;
|
int high = charToByte(normalizedHex.charAt(pos));
|
int low = charToByte(normalizedHex.charAt(pos + 1));
|
result[i] = (byte) ((high << 4) | low);
|
}
|
return result;
|
}
|
|
/**
|
* 字符转换为字节
|
*
|
* @param c 字符
|
* @return 字节值
|
*/
|
private static byte charToByte(char c) {
|
return (byte) HEX_CHARS.indexOf(c);
|
}
|
|
/**
|
* 整数转换为4字节数组(大端序)
|
*
|
* @param num 整数
|
* @return 4字节数组
|
*/
|
public static byte[] intToBytes(int num) {
|
byte[] bytes = new byte[4];
|
for (int i = 0; i < 4; i++) {
|
bytes[i] = (byte) (num >>> (24 - i * 8));
|
}
|
return bytes;
|
}
|
|
/**
|
* 字符串转换为十六进制字符串
|
*
|
* @param str 输入字符串
|
* @return 十六进制字符串
|
*/
|
public static String strToHexStr(String str) {
|
if (str == null) return "";
|
|
StringBuilder sb = new StringBuilder();
|
byte[] bytes = str.getBytes();
|
for (byte b : bytes) {
|
sb.append(HEX_CHARS.charAt((b & 0xF0) >> 4));
|
sb.append(HEX_CHARS.charAt(b & 0x0F));
|
}
|
return sb.toString();
|
}
|
|
/**
|
* 计算Modbus CRC-16校验码(返回大写十六进制字符串)
|
*
|
* @param data 输入数据
|
* @return 4位大写的十六进制CRC字符串
|
*/
|
public static String calculate(byte[] data) {
|
int crc = 0xFFFF;
|
for (byte b : data) {
|
crc ^= b & 0xFF;
|
for (int i = 0; i < 8; i++) {
|
if ((crc & 1) == 1) {
|
crc = (crc >>> 1) ^ 0xA001;
|
} else {
|
crc = crc >>> 1;
|
}
|
}
|
}
|
// 使用大写格式,确保4位长度,不足补零
|
return String.format("%04X", crc);
|
}
|
|
/**
|
* BCD码转为字符串
|
*
|
* @param bytes BCD码字节数组
|
* @return 十进制字符串
|
*/
|
public static String bcdToStr(byte[] bytes) {
|
if (bytes == null || bytes.length == 0) {
|
return "";
|
}
|
|
StringBuilder temp = new StringBuilder(bytes.length * 2);
|
for (byte b : bytes) {
|
temp.append((b & 0xF0) >>> 4);
|
temp.append(b & 0x0F);
|
}
|
|
// 去除前导零
|
String result = temp.toString();
|
return result.startsWith("0") ? result.substring(1) : result;
|
}
|
|
/**
|
* 字符串转为BCD码
|
*
|
* @param asc 十进制字符串
|
* @return BCD码字节数组
|
*/
|
public static byte[] strToBcd(String asc) {
|
if (asc == null || asc.isEmpty()) {
|
return new byte[0];
|
}
|
|
// 确保长度为偶数
|
String normalized = asc.length() % 2 != 0 ? "0" + asc : asc;
|
byte[] result = new byte[normalized.length() / 2];
|
|
for (int i = 0; i < result.length; i++) {
|
char highChar = normalized.charAt(2 * i);
|
char lowChar = normalized.charAt(2 * i + 1);
|
|
int high = charToBcdValue(highChar);
|
int low = charToBcdValue(lowChar);
|
|
result[i] = (byte) ((high << 4) | low);
|
}
|
return result;
|
}
|
|
private static int charToBcdValue(char c) {
|
if (c >= '0' && c <= '9') {
|
return c - '0';
|
} else if (c >= 'a' && c <= 'f') {
|
return c - 'a' + 10;
|
} else if (c >= 'A' && c <= 'F') {
|
return c - 'A' + 10;
|
} else {
|
throw new IllegalArgumentException("无效的BCD字符: " + c);
|
}
|
}
|
|
/**
|
* 反转字节数组
|
*
|
* @param data 字节数组
|
* @param len 要反转的长度
|
*/
|
public static void reverse(byte[] data, int len) {
|
if (data == null || len <= 1) return;
|
|
for (int i = 0; i < len / 2; i++) {
|
byte temp = data[i];
|
data[i] = data[len - 1 - i];
|
data[len - 1 - i] = temp;
|
}
|
}
|
|
/**
|
* 字节数组转换为字符串(遇到0x00终止)
|
*
|
* @param data 字节数组
|
* @return 字符串
|
*/
|
public static String bytesToString(byte[] data) {
|
if (data == null || data.length == 0) {
|
return "";
|
}
|
|
int length = 0;
|
for (int i = 0; i < data.length; i++) {
|
if (data[i] == 0) {
|
length = i;
|
break;
|
}
|
length = data.length;
|
}
|
|
return new String(data, 0, length);
|
}
|
|
/**
|
* 获取异常的堆栈跟踪信息
|
*
|
* @param e 异常
|
* @return 格式化的异常信息
|
*/
|
public static String getExceptionTrace(Exception e) {
|
if (e == null) return "";
|
|
StringBuilder message = new StringBuilder("异常消息: " + e.getMessage());
|
|
StackTraceElement[] stackTrace = e.getStackTrace();
|
if (stackTrace != null && stackTrace.length > 0) {
|
StackTraceElement firstElement = stackTrace[0];
|
message.append(" 报错位置: ")
|
.append(firstElement.getClassName()).append(".")
|
.append(firstElement.getMethodName()).append("(")
|
.append(firstElement.getFileName()).append(":")
|
.append(firstElement.getLineNumber()).append(")");
|
}
|
return message.toString();
|
}
|
|
// 以下方法保持原有功能,但建议使用Java标准库替代
|
// 由于时间关系,这里只优化主要方法
|
|
/**
|
* 字节数组(去掉后面的0)转Unicode字符串
|
* 注意:此方法逻辑较复杂,建议在实际使用中验证
|
*/
|
public static String bytesToUnicodeString(byte[] src) {
|
// 保持原有实现,但建议重构
|
if (src == null || src.length == 0) {
|
return "";
|
}
|
|
int strlen = 0;
|
for (int j = src.length - 1; j >= 0; j--) {
|
if (src[j] != 0) {
|
strlen = j + 1;
|
break;
|
}
|
}
|
|
if (strlen == 0) {
|
return "";
|
}
|
|
// 确保长度为偶数
|
if (strlen % 2 != 0) {
|
strlen++;
|
}
|
|
byte[] strData = Arrays.copyOfRange(src, 0, strlen);
|
try {
|
// 交换字节序
|
swapBytes(strData);
|
return new String(strData, "UNICODE");
|
} catch (UnsupportedEncodingException e) {
|
throw new RuntimeException("不支持的编码: UNICODE", e);
|
}
|
}
|
|
private static void swapBytes(byte[] data) {
|
for (int i = 0; i < data.length / 2; i++) {
|
byte temp = data[2 * i];
|
data[2 * i] = data[2 * i + 1];
|
data[2 * i + 1] = temp;
|
}
|
}
|
}
|