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; } } }