Как преобразовать массив байтов в шестнадцатеричную строку в Java?
Правильное преобразование byte[] в hex-строку
Ваша интуиция использовать Integer.toHexString() верна, но есть несколько нюансов, которые нужно учесть. Основные проблемы вашего кода:
- Для значений от 0 до 15 (0x0-0xF) метод возвращает один символ, а не два.
- Для отрицательных байтов (старший бит установлен) требуется корректная маскировка.
Решение №1: Использование StringBuilder с форматированием
Самый надёжный способ — использовать String.format() или Formatter для гарантированного вывода двух символов:
public static String bytesToHex(byte[] bytes) {
StringBuilder sb = new StringBuilder();
for (byte b : bytes) {
// & 0xFF убирает знаковое расширение для отрицательных байтов
// %02x — формат: два шестнадцатеричных символа, с ведущим нулём
sb.append(String.format("%02x", b & 0xFF));
}
return sb.toString();
}
Объяснение:
b & 0xFF— побитовое И с 0xFF (255) преобразует отрицательный байт в положительное целое число от 0 до 255. Например,(byte) -1 & 0xFF = 255.%02x— спецификатор формата:0— дополнять нулями слева,2— минимальная ширина в 2 символа,x— шестнадцатеричное число в нижнем регистре.
Решение №2: Использование javax.xml.bind.DatatypeConverter (Java 8 и старше)
Если вы используете Java 8 или более раннюю версию, есть встроенный класс:
import javax.xml.bind.DatatypeConverter;
public static String bytesToHex(byte[] bytes) {
return DatatypeConverter.printHexBinary(bytes);
}
Этот метод возвращает строку в верхнем регистре (например, "0AFF1B").
Решение №3: Использование java.util.HexFormat (Java 17+)
Начиная с Java 17, появился официальный способ:
public static String bytesToHex(byte[] bytes) {
return HexFormat.of().formatHex(bytes);
}
По умолчанию возвращает строку в нижнем регистре. Можно настроить:
HexFormat.of().withUpperCase().formatHex(bytes); // верхний регистр
Решение №4: Оптимизированная ручная реализация
Если вам нужно максимальное быстродействие (например, для больших массивов):
private static final char[] HEX_ARRAY = "0123456789abcdef".toCharArray();
public static String bytesToHex(byte[] bytes) {
char[] hexChars = new char[bytes.length * 2];
for (int i = 0; i < bytes.length; i++) {
int v = bytes[i] & 0xFF;
hexChars[i * 2] = HEX_ARRAY[v >>> 4];
hexChars[i * 2 + 1] = HEX_ARRAY[v & 0x0F];
}
return new String(hexChars);
}
Как это работает:
v >>> 4— сдвиг вправо на 4 бита, получаем старший полубайт (0-15).v & 0x0F— маскировка, получаем младший полубайт.- Используем предварительно заполненный массив символов для конвертации цифры в символ.
Тестирование
byte[] test = {0x0A, (byte) 0xFF, 0x1B, 0x00, 0x7F};
System.out.println(bytesToHex(test)); // "0aff1b007f" (нижний регистр)
Рекомендация
Для современных проектов (Java 17+) используйте HexFormat. Для более старых версий — DatatypeConverter или оптимизированную ручную реализацию. Избегайте String.format() внутри цикла для больших массивов из-за накладных расходов на создание множества промежуточных строк.
Альтернативный подход: Использование BigInteger
Если ваш массив байт небольшой (до 16-32 байт), можно использовать BigInteger для преобразования:
public static String bytesToHex(byte[] bytes) {
// Создаём BigInteger из массива байт (с учётом знака)
BigInteger bigInt = new BigInteger(1, bytes);
// Преобразуем в hex-строку
String hex = bigInt.toString(16);
// Дополняем нулями слева, если нужно
int expectedLength = bytes.length * 2;
while (hex.length() < expectedLength) {
hex = "0" + hex;
}
return hex;
}
Плюсы:
- Короткий и читаемый код.
- Не требует ручной обработки каждого байта.
Минусы:
- Медленнее для больших массивов (BigInteger создаёт объект).
- Требуется ручное дополнение нулями, если ведущие байты равны нулю.
- Конструктор
BigInteger(1, bytes)создаёт копию массива, что может быть неэффективно.
Этот способ хорошо подходит для одноразовых преобразований или отладки, но не рекомендуется для высоконагруженных систем.
Ответов пока нет.
Войдите чтобы оставить ответ