Java-код Для преобразования байта в шестнадцатеричный - PullRequest
168 голосов
/ 12 мая 2010

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

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

Ответы [ 19 ]

284 голосов
/ 12 мая 2010
    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 более предпочтительным.

Ссылки

59 голосов
/ 17 января 2014

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

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

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

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


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

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

Примеры * * тысяча двадцать три Пример 1: Преобразование 2 в шестнадцатеричное. 1) Сначала преобразовать 2 в двоичный код в дополнении к двум: 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 в двоичный код в дополнении до двух: -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 Теперь, когда мы рассмотрели эту концепцию, вы обнаружите, что мы можем достичь того, чего хотим, с помощью простой маскировки и сдвига. Главное, что нужно понять, это то, что байт, который вы пытаетесь преобразовать, уже находится в дополнении к двум. Вы не делаете это преобразование самостоятельно. Я думаю, что это серьезная путаница в этом вопросе. Возьмем, к примеру, следующий байтовый массив: 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)); } Надеюсь, это объяснение прояснит ситуацию для тех, кто интересуется, что именно происходит во многих примерах, которые вы найдете в Интернете. Надеюсь, я не допустил вопиющих ошибок, но предложения и исправления приветствуются!

21 голосов
/ 17 ноября 2014

Способ самый быстрый , который я пока нашел, заключается в следующем:

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);
15 голосов
/ 12 мая 2010

Попробуйте так:

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() (поскольку он правильно обрабатывает отрицательные числа).

13 голосов
/ 13 декабря 2016

Лучшее решение - это крутая однострочная:

String hex=DatatypeConverter.printHexBinary(byte[] b);

как уже упоминалось здесь

8 голосов
/ 12 мая 2010

Если вам нужно шестнадцатеричное представление постоянной ширины, то есть 0A вместо A, чтобы вы могли однозначно восстановить байты, попробуйте format():

StringBuilder result = new StringBuilder();
for (byte bb : byteArray) {
    result.append(String.format("%02X", bb));
}
return result.toString();
7 голосов
/ 03 сентября 2013

Если вы счастливы использовать внешнюю библиотеку, класс org.apache.commons.codec.binary.Hex имеет метод encodeHex, который принимает byte[] и возвращает char[]. Этот метод НАМНОГО быстрее, чем опция формата, и включает в себя детали преобразования. Также поставляется с методом decodeHex для обратного преобразования.

6 голосов
/ 25 января 2019

Короткий и простой способ преобразования byte[] в шестнадцатеричную строку с помощью BigInteger:

import java.math.BigInteger;

byte[] bytes = new byte[] {(byte)255, 10, 20, 30};
String hex = new BigInteger(1, bytes).toString(16);
System.out.println(hex); // ff0a141e

Как это работает?

Встроенный системный класс java.math.BigInteger ( java.math.BigInteger ) совместим с двоичными и шестнадцатеричными данными:

  • Имеет конструктор BigInteger(signum=1, byte[]) для создания большого целого числа на byte[] (установите его первый параметр signum = 1 для правильной обработки отрицательных байтов)
  • Используйте BigInteger.toString(16) для преобразования большого целого числа в hex string
  • Для разбора шестнадцатеричного числа используйте new BigInteger("ffa74b", 16) - неправильно обрабатывает начальный ноль

Если вы хотите иметь начальный ноль в шестнадцатеричном результате, проверьте его размер и добавьте недостающий ноль, если необходимо:

if (hex.length() % 2 == 1)
    hex = "0" + hex;

Примечания

Используйте new BigInteger(1, bytes) вместо new BigInteger(bytes), потому что Java " не соответствует конструкции ", а тип данных byte не содержит байтов, но имеет крошечные целые числа со знаком [-128 ... 127 ]. Если первый байт отрицательный, BigInteger предполагает, что вы передаете отрицательное большое целое число. Просто передайте 1 в качестве первого параметра (signum=1).

Преобразование обратно из шестнадцатеричного числа в byte[] является хитрым: иногда ведущий ноль входит в произведенный вывод, и его следует очистить следующим образом:

byte[] bytes = new BigInteger("ffa74b", 16).toByteArray();
if (bytes[0] == 0) {
    byte[] newBytes = new byte[bytes.length - 1];
    System.arraycopy(bytes, 1, newBytes, 0, newBytes.length);
    bytes = newBytes;
}

Последнее замечание: если byte[] имеет несколько ведущих нулей, они будут потеряны.

5 голосов
/ 31 января 2017

Вы можете использовать метод из Поставщик Bouncy Castle Библиотека:

org.bouncycastle.util.encoders.Hex.toHexString(byteArray);

Пакет Bouncy Castle Crypto является реализацией Java криптографические алгоритмы. Эта банка содержит провайдера JCE и облегченный API для API шифрования Bouncy Castle для JDK 1.5 до JDK 1.8.

Зависимость Maven:

<dependency>
    <groupId>org.bouncycastle</groupId>
    <artifactId>bcprov-jdk15on</artifactId>
    <version>1.60</version>
</dependency>

или Кодек Apache Commons :

org.apache.commons.codec.binary.Hex.encodeHexString(byteArray);

Пакет кодеков Apache Commons содержит простой кодер и декодеры для различных форматов, таких как Base64 и Hexadecimal. В дополнение к эти широко используемые кодеры и декодеры, пакет кодеков также поддерживает набор утилит фонетического кодирования.

Зависимость Maven:

<dependency>
    <groupId>commons-codec</groupId>
    <artifactId>commons-codec</artifactId>
    <version>1.11</version>
</dependency>
5 голосов
/ 20 августа 2015

Это код, который я нашел для запуска быстрее всего. Я запускал его на 109015 байтовых массивах длиной 32 в 23 мс. Я запустил его на виртуальной машине, поэтому, вероятно, он будет работать быстрее на голом металле.

public static final char[] HEX_DIGITS =         {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};

public static char[] encodeHex( final byte[] data ){
    final int l = data.length;
    final char[] out = new char[l<<1];
    for( int i=0,j=0; i<l; i++ ){
        out[j++] = HEX_DIGITS[(0xF0 & data[i]) >>> 4];
        out[j++] = HEX_DIGITS[0x0F & data[i]];
    }
    return out;
}

Тогда вы можете просто сделать

String s = new String( encodeHex(myByteArray) );
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...