Вопрос-ответ

How can I convert a byte array to hexadecimal in Java?

Как я могу преобразовать массив байтов в шестнадцатеричный в Java?

У меня есть массив байтов. Я хочу, чтобы каждая строка байтов этого массива была преобразована в соответствующее шестнадцатеричное значение.

Есть ли в Java какая-либо функция для преобразования массива байтов в шестнадцатеричный?

Переведено автоматически
Ответ 1
byte[] bytes = {-1, 0, 1, 2, 3 };
StringBuilder sb = new StringBuilder();
for (byte b : bytes) {
sb.append(String.format("%02X ", b));
}
System.out.println(sb.toString());
// prints "FF 00 01 02 03 "

Смотрите также


  • java.util.Formatter синтаксис

    • %[flags][width]conversion

      • Флаг '0' - результат будет дополнен нулем

      • Ширина 2

      • Преобразование 'X' - Результат форматируется как шестнадцатеричное целое число в верхнем регистре






Глядя на текст вопроса, также возможно, что это то, что запрашивается:

String[] arr = {"-1", "0", "10", "20" };
for (int i = 0; i < arr.length; i++) {
arr[i] = String.format("%02x", Byte.parseByte(arr[i]));
}
System.out.println(java.util.Arrays.toString(arr));
// prints "[ff, 00, 0a, 14]"

Здесь используется несколько ответов Integer.toHexString(int); это выполнимо, но с некоторыми оговорками. Поскольку параметром является int, в byte аргумент выполняется расширяющее примитивное преобразование, которое включает расширение знака.

byte b = -1;
System.out.println(Integer.toHexString(b));
// prints "ffffffff"

8-разрядный byte, который подписан в Java, расширен до 32-разрядного int. Чтобы эффективно отменить это расширение знака, можно замаскировать byte с помощью 0xFF.

byte b = -1;
System.out.println(Integer.toHexString(b & 0xFF));
// prints "ff"

Еще одна проблема с использованием toHexString заключается в том, что он не заполняется нулями:

byte b = 10;
System.out.println(Integer.toHexString(b & 0xFF));
// prints "a"

Оба фактора в совокупности должны сделать String.format решение более предпочтительным.

Ссылки

Ответ 2

Я публикую, потому что ни один из существующих ответов не объясняет, почему их подходы работают, что, я думаю, действительно важно для этой проблемы. В некоторых случаях это приводит к тому, что предлагаемое решение кажется излишне сложным и утонченным. Для иллюстрации я приведу довольно простой подход, но я приведу немного больше деталей, чтобы помочь проиллюстрировать, почему это работает.

Прежде всего, что мы пытаемся сделать? Мы хотим преобразовать значение в байтах (или массив байтов) в строку, которая представляет шестнадцатеричное значение в ASCII. Итак, шаг первый - точно выяснить, что такое байт в Java:


Тип данных byte - это 8-разрядное целое число со знаком, дополняющее двойку. Оно имеет минимальное значение -128 и максимальное значение 127 (включительно). Тип данных byte может быть полезен для экономии памяти в больших массивах, где экономия памяти действительно имеет значение. Они также могут использоваться вместо int, где их ограничения помогают прояснить ваш код; тот факт, что диапазон переменной ограничен, может служить формой документации.


Что это значит? Несколько вещей: Во-первых, и это самое главное, это означает, что мы работаем с 8 битами. Так, например, мы можем записать число 2 как 0000 0010. Однако, поскольку это дополнение two, мы записываем отрицательное значение 2 следующим образом: 1111 1110. Это также означает, что преобразование в шестнадцатеричный формат очень простое. То есть вы просто преобразуете каждый 4-битный сегмент непосредственно в шестнадцатеричный. Обратите внимание, что для понимания смысла отрицательных чисел в этой схеме вам сначала нужно понять дополнение two . Если вы еще не понимаете дополнение two, вы можете прочитать отличное объяснение здесь: http://www.cs.cornell.edu / ~tomf/notes/cps104/twoscomp.html


Преобразование дополнения Two в шестнадцатеричный в общих чертах

Как только число дополняется двумя, преобразовать его в шестнадцатеричный очень просто. В общем, преобразование из двоичного файла в шестнадцатеричный очень простое, и, как вы увидите в следующих двух примерах, вы можете перейти непосредственно от дополнения two к шестнадцатеричному.

Примеры

Пример 1: Преобразовать 2 в шестнадцатеричный.

1) Сначала преобразуйте 2 в двоичный в дополнении two:

2 (base 10) = 0000 0010 (base 2)

2) Теперь преобразуйте двоичный код в шестнадцатеричный:

0000 = 0x0 in hex
0010 = 0x2 in hex

therefore 2 = 0000 0010 = 0x02.

Пример 2: Преобразовать -2 (в дополнение к двум) в шестнадцатеричный.

1) Сначала преобразуйте -2 в двоичный файл в дополнении two:

-2 (base 10) = 0000 0010 (direct conversion to binary) 
1111 1101 (invert bits)
1111 1110 (add 1)
therefore: -2 = 1111 1110 (in two's complement)

2) Теперь преобразуем в шестнадцатеричный:

1111 = 0xF in hex
1110 = 0xE in hex

therefore: -2 = 1111 1110 = 0xFE.


Как это сделать в Java

Теперь, когда мы рассмотрели концепцию, вы обнаружите, что мы можем достичь желаемого с помощью простой маскировки и сдвига. Главное, что нужно понимать, это то, что байт, который вы пытаетесь преобразовать, уже находится в дополнении two. Вы не выполняете это преобразование самостоятельно. Я думаю, что это основная путаница в этом вопросе. Возьмем, к примеру, следующий массив байтов:

byte[] bytes = new byte[]{-2,2};

Мы просто вручную преобразовали их в шестнадцатеричный, как указано выше, но как мы можем сделать это в Java? Вот как:

Шаг 1: Создаем StringBuffer для хранения наших вычислений.

StringBuffer buffer = new StringBuffer();

Шаг 2: Выделите биты более высокого порядка, преобразуйте их в шестнадцатеричный и добавьте их в буфер

Учитывая двоичное число 1111 1110, мы можем выделить биты более высокого порядка, сначала сдвинув их на 4, а затем обнулив остальную часть числа. Логически это просто, однако детали реализации в Java (и многих языках) создают проблему из-за расширения знака. По сути, когда вы сдвигаете значение в байтах, Java сначала преобразует ваше значение в целое число, а затем выполняет расширение знака. Итак, хотя вы ожидаете, что 1111 1110 >> 4 будет 0000 1111, на самом деле в Java он представлен как дополнение двух 0xFFFFFFFF!

Итак, возвращаясь к нашему примеру:

1111 1110 >> 4 (shift right 4) = 1111 1111 1111 1111 1111 1111 1111 1111 (32 bit sign-extended number in two's complement)

Затем мы можем изолировать биты с помощью маски:

1111 1111 1111 1111 1111 1111 1111 1111 & 0xF = 0000 0000 0000 0000 0000 0000 0000 1111
therefore: 1111 = 0xF in hex.

В Java мы можем сделать все это одним выстрелом:

Character.forDigit((bytes[0] >> 4) & 0xF, 16);

Функция forDigit просто сопоставляет число, которое вы передаете, с набором шестнадцатеричных чисел 0-F.

Шаг 3: Далее нам нужно изолировать биты младшего порядка. Поскольку нужные нам биты уже находятся в правильном положении, мы можем просто замаскировать их:

1111 1110 & 0xF = 0000 0000 0000 0000 0000 0000 0000 1110 (recall sign extension from before)
therefore: 1110 = 0xE in hex.

Как и раньше, в Java мы можем сделать все это одним выстрелом:

Character.forDigit((bytes[0] & 0xF), 16);

Собрав все это вместе, мы можем сделать это как цикл for и преобразовать весь массив целиком:

for(int i=0; i < bytes.length; i++){
buffer.append(Character.forDigit((bytes[i] >> 4) & 0xF, 16));
buffer.append(Character.forDigit((bytes[i] & 0xF), 16));
}

Надеюсь, это объяснение прояснит ситуацию для тех из вас, кому интересно, что именно происходит во многих примерах, которые вы найдете в Интернете. Надеюсь, я не допустил каких-либо вопиющих ошибок, но предложения и исправления очень приветствуются!

Ответ 3

Самый быстрый способ, который я пока нашел для этого, следующий:

private static final String    HEXES    = "0123456789ABCDEF";

static String getHex(byte[] raw) {
final StringBuilder hex = new StringBuilder(2 * raw.length);
for (final byte b : raw) {
hex.append(HEXES.charAt((b & 0xF0) >> 4)).append(HEXES.charAt((b & 0x0F)));
}
return hex.toString();
}

Это в ~ 50 раз быстрее, чем String.format. если вы хотите протестировать это:

public class MyTest{
private static final String HEXES = "0123456789ABCDEF";

@Test
public void test_get_hex() {
byte[] raw = {
(byte) 0xd0, (byte) 0x0b, (byte) 0x01, (byte) 0x2a, (byte) 0x63,
(byte) 0x78, (byte) 0x01, (byte) 0x2e, (byte) 0xe3, (byte) 0x6c,
(byte) 0xd2, (byte) 0xb0, (byte) 0x78, (byte) 0x51, (byte) 0x73,
(byte) 0x34, (byte) 0xaf, (byte) 0xbb, (byte) 0xa0, (byte) 0x9f,
(byte) 0xc3, (byte) 0xa9, (byte) 0x00, (byte) 0x1e, (byte) 0xd5,
(byte) 0x4b, (byte) 0x89, (byte) 0xa3, (byte) 0x45, (byte) 0x35,
(byte) 0xd6, (byte) 0x10,
};

int N = 77777;
long t;

{
t = System.currentTimeMillis();
for (int i = 0; i < N; i++) {
final StringBuilder hex = new StringBuilder(2 * raw.length);
for (final byte b : raw) {
hex.append(HEXES.charAt((b & 0xF0) >> 4)).append(HEXES.charAt((b & 0x0F)));
}
hex.toString();
}
System.out.println(System.currentTimeMillis() - t); // 50
}

{
t = System.currentTimeMillis();
for (int i = 0; i < N; i++) {
StringBuilder hex = new StringBuilder(2 * raw.length);
for (byte b : raw) {
hex.append(String.format("%02X", b));
}
hex.toString();
}
System.out.println(System.currentTimeMillis() - t); // 2535
}

}
}

Редактировать: только что нашел кое-что немного быстрее, и это удерживается на одной строке, но не совместимо с JRE 9. Используйте на свой страх и риск

import javax.xml.bind.DatatypeConverter;

DatatypeConverter.printHexBinary(raw);
Ответ 4

Попробуйте этот способ:

byte bv = 10;
String hexString = Integer.toHexString(bv);

Работа с массивом (если я вас правильно понял):

byte[] bytes = {9, 10, 11, 15, 16};
StringBuffer result = new StringBuffer();
for (byte b : bytes) {
result.append(String.format("%02X ", b));
result.append(" "); // delimiter
}
return result.toString();

Как упоминали полигенные поставщики, String.format() это правильный ответ по сравнению с Integer.toHexString() (поскольку он правильно обрабатывает отрицательные числа).

java