Код Java для преобразования байта в шестнадцатеричный

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

Есть ли какая-либо функция в 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:

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

Что это значит? Несколько вещей: во-первых, и самое главное, это означает, что мы работаем с 8-бит. Так, например, мы можем записать число 2 как 0000 0010. Однако, поскольку это два дополнения, мы пишем отрицательный 2 следующим образом: 1111 1110. Что также означает, что преобразование в hex очень просто. То есть вы просто конвертируете каждый 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 (в два дополнения) в Hex.

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

    }
}

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

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

Ответ 5

Лучшее решение - это однострочный зачет:

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

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

Ответ 6

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

StringBuilder result = new StringBuilder();
for (byte bb : byteArray) {
    result.append(String.format("%02X", bb));
}
return result.toString();

Ответ 7

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

Ответ 8

Это код, который я нашел для запуска быстрее всего. Я запустил его на 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) );

Ответ 9

BigInteger n = new BigInteger(byteArray);
String hexa = n.toString(16));

Ответ 10

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

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

Пакет Bouncy Castle Crypto - это Java-реализация криптографических алгоритмов. Этот jar содержит провайдера 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>

Ответ 11

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

   private static String convertToHex(byte[] data) {
    StringBuffer buf = new StringBuffer();
    for (int i = 0; i < data.length; i++) {
        int halfbyte = (data[i] >>> 4) & 0x0F;
        int two_halfs = 0;
        do {
            if ((0 <= halfbyte) && (halfbyte <= 9))
                buf.append((char) ('0' + halfbyte));
            else
                buf.append((char) ('a' + (halfbyte - 10)));
            halfbyte = data[i] & 0x0F;
        } while(two_halfs++ < 1);
    }
    return buf.toString();
}

Ответ 12

Другие рассмотрели общий случай. Но если у вас есть массив байтов известной формы, например MAC-адрес, вы можете:

byte[] mac = { (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00 };

String str = String.format("%02X:%02X:%02X:%02X:%02X:%02X",
                           mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); 

Ответ 13

Создание (и уничтожение) кучи экземпляров String не является хорошим способом, если производительность является проблемой.

Пожалуйста, проигнорируйте те подробные (дублирующие) аргументы проверки аргументов (if s). Это для (других) образовательных целей.

Полный проект maven: http://jinahya.googlecode.com/svn/trunk/com.googlecode.jinahya/hex-codec/

Кодировка...

/**
 * Encodes a single nibble.
 *
 * @param decoded the nibble to encode.
 *
 * @return the encoded half octet.
 */
protected static int encodeHalf(final int decoded) {

    switch (decoded) {
        case 0x00:
        case 0x01:
        case 0x02:
        case 0x03:
        case 0x04:
        case 0x05:
        case 0x06:
        case 0x07:
        case 0x08:
        case 0x09:
            return decoded + 0x30; // 0x30('0') - 0x39('9')
        case 0x0A:
        case 0x0B:
        case 0x0C:
        case 0x0D:
        case 0x0E:
        case 0x0F:
            return decoded + 0x57; // 0x41('a') - 0x46('f')
        default:
            throw new IllegalArgumentException("illegal half: " + decoded);
    }
}


/**
 * Encodes a single octet into two nibbles.
 *
 * @param decoded the octet to encode.
 * @param encoded the array to which each encoded nibbles are written.
 * @param offset the offset in the array.
 */
protected static void encodeSingle(final int decoded, final byte[] encoded,
                                   final int offset) {

    if (encoded == null) {
        throw new IllegalArgumentException("null encoded");
    }

    if (encoded.length < 2) {
        // not required
        throw new IllegalArgumentException(
            "encoded.length(" + encoded.length + ") < 2");
    }

    if (offset < 0) {
        throw new IllegalArgumentException("offset(" + offset + ") < 0");
    }

    if (offset >= encoded.length - 1) {
        throw new IllegalArgumentException(
            "offset(" + offset + ") >= encoded.length(" + encoded.length
            + ") - 1");
    }

    encoded[offset] = (byte) encodeHalf((decoded >> 4) & 0x0F);
    encoded[offset + 1] = (byte) encodeHalf(decoded & 0x0F);
}


/**
 * Decodes given sequence of octets into a sequence of nibbles.
 *
 * @param decoded the octets to encode
 *
 * @return the encoded nibbles.
 */
protected static byte[] encodeMultiple(final byte[] decoded) {

    if (decoded == null) {
        throw new IllegalArgumentException("null decoded");
    }

    final byte[] encoded = new byte[decoded.length << 1];

    int offset = 0;
    for (int i = 0; i < decoded.length; i++) {
        encodeSingle(decoded[i], encoded, offset);
        offset += 2;
    }

    return encoded;
}


/**
 * Encodes given sequence of octets into a sequence of nibbles.
 *
 * @param decoded the octets to encode.
 *
 * @return the encoded nibbles.
 */
public byte[] encode(final byte[] decoded) {

    return encodeMultiple(decoded);
}

Декодирование...

/**
 * Decodes a single nibble.
 *
 * @param encoded the nibble to decode.
 *
 * @return the decoded half octet.
 */
protected static int decodeHalf(final int encoded) {

    switch (encoded) {
        case 0x30: // '0'
        case 0x31: // '1'
        case 0x32: // '2'
        case 0x33: // '3'
        case 0x34: // '4'
        case 0x35: // '5'
        case 0x36: // '6'
        case 0x37: // '7'
        case 0x38: // '8'
        case 0x39: // '9'
            return encoded - 0x30;
        case 0x41: // 'A'
        case 0x42: // 'B'
        case 0x43: // 'C'
        case 0x44: // 'D'
        case 0x45: // 'E'
        case 0x46: // 'F'
            return encoded - 0x37;
        case 0x61: // 'a'
        case 0x62: // 'b'
        case 0x63: // 'c'
        case 0x64: // 'd'
        case 0x65: // 'e'
        case 0x66: // 'f'
            return encoded - 0x57;
        default:
            throw new IllegalArgumentException("illegal half: " + encoded);
    }
}


/**
 * Decodes two nibbles into a single octet.
 *
 * @param encoded the nibble array.
 * @param offset the offset in the array.
 *
 * @return decoded octet.
 */
protected static int decodeSingle(final byte[] encoded, final int offset) {

    if (encoded == null) {
        throw new IllegalArgumentException("null encoded");
    }

    if (encoded.length < 2) {
        // not required
        throw new IllegalArgumentException(
            "encoded.length(" + encoded.length + ") < 2");
    }

    if (offset < 0) {
        throw new IllegalArgumentException("offset(" + offset + ") < 0");
    }

    if (offset >= encoded.length - 1) {
        throw new IllegalArgumentException(
            "offset(" + offset + ") >= encoded.length(" + encoded.length
            + ") - 1");
    }

    return (decodeHalf(encoded[offset]) << 4)
           | decodeHalf(encoded[offset + 1]);
}


/**
 * Encodes given sequence of nibbles into a sequence of octets.
 *
 * @param encoded the nibbles to decode.
 *
 * @return the encoded octets.
 */
protected static byte[] decodeMultiple(final byte[] encoded) {

    if (encoded == null) {
        throw new IllegalArgumentException("null encoded");
    }

    if ((encoded.length & 0x01) == 0x01) {
        throw new IllegalArgumentException(
            "encoded.length(" + encoded.length + ") is not even");
    }

    final byte[] decoded = new byte[encoded.length >> 1];

    int offset = 0;
    for (int i = 0; i < decoded.length; i++) {
        decoded[i] = (byte) decodeSingle(encoded, offset);
        offset += 2;
    }

    return decoded;
}


/**
 * Decodes given sequence of nibbles into a sequence of octets.
 *
 * @param encoded the nibbles to decode.
 *
 * @return the decoded octets.
 */
public byte[] decode(final byte[] encoded) {

    return decodeMultiple(encoded);
}

Ответ 14

Я не мог понять, что именно вы имели в виду byte String, но вот некоторые преобразования из байта в String и наоборот, конечно, в официальных документах есть намного больше

Integer intValue = 149;

Соответствующее значение байта:

Byte byteValue = intValue.byteValue(); // this will convert the rightmost byte of the intValue to byte, because Byte is an 8 bit object and Integer is at least 16 bit, and it will give you a signed number in this case -107

получить целочисленное значение обратно из байтовой переменной:

Integer anInt = byteValue.intValue(); // This will convert the byteValue variable to a signed Integer

От байта и целого до шестнадцатеричной строки:
Так я это делаю:

Integer anInt = 149
Byte aByte = anInt.byteValue();

String hexFromInt = "".format("0x%x", anInt); // This will output 0x95
String hexFromByte = "".format("0x%x", aByte); // This will output 0x95

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

От байта [] до строки:

    public static String byteArrayToHexString(byte[] byteArray){
        String hexString = "";

        for(int i = 0; i < byteArray.length; i++){
            String thisByte = "".format("%x", byteArray[i]);
            hexString += thisByte;
        }

        return hexString;
    }

И от шестнадцатеричной строки до байта []:

public static byte[] hexStringToByteArray(String hexString){
    byte[] bytes = new byte[hexString.length() / 2];

    for(int i = 0; i < hexString.length(); i += 2){
        String sub = hexString.substring(i, i + 2);
        Integer intVal = Integer.parseInt(sub, 16);
        bytes[i / 2] = intVal.byteValue();
        String hex = "".format("0x%x", bytes[i / 2]);
    }

    return bytes;
}  

Уже слишком поздно, но я надеюсь, что это может помочь некоторым другим;)

Ответ 15

Это очень быстрый способ. Никаких внешних библиотек не требуется.

final protected static char[] HEXARRAY = "0123456789abcdef".toCharArray();

    public static String encodeHexString( byte[] bytes ) {

        char[] hexChars = new char[bytes.length * 2];
        for (int j = 0; j < bytes.length; j++) {
            int v = bytes[j] & 0xFF;
            hexChars[j * 2] = HEXARRAY[v >>> 4];
            hexChars[j * 2 + 1] = HEXARRAY[v & 0x0F];
        }
        return new String(hexChars);
    }

Ответ 16

Там ваш быстрый метод:

    private static final String[] hexes = new String[]{
        "00","01","02","03","04","05","06","07","08","09","0A","0B","0C","0D","0E","0F",
        "10","11","12","13","14","15","16","17","18","19","1A","1B","1C","1D","1E","1F",
        "20","21","22","23","24","25","26","27","28","29","2A","2B","2C","2D","2E","2F",
        "30","31","32","33","34","35","36","37","38","39","3A","3B","3C","3D","3E","3F",
        "40","41","42","43","44","45","46","47","48","49","4A","4B","4C","4D","4E","4F",
        "50","51","52","53","54","55","56","57","58","59","5A","5B","5C","5D","5E","5F",
        "60","61","62","63","64","65","66","67","68","69","6A","6B","6C","6D","6E","6F",
        "70","71","72","73","74","75","76","77","78","79","7A","7B","7C","7D","7E","7F",
        "80","81","82","83","84","85","86","87","88","89","8A","8B","8C","8D","8E","8F",
        "90","91","92","93","94","95","96","97","98","99","9A","9B","9C","9D","9E","9F",
        "A0","A1","A2","A3","A4","A5","A6","A7","A8","A9","AA","AB","AC","AD","AE","AF",
        "B0","B1","B2","B3","B4","B5","B6","B7","B8","B9","BA","BB","BC","BD","BE","BF",
        "C0","C1","C2","C3","C4","C5","C6","C7","C8","C9","CA","CB","CC","CD","CE","CF",
        "D0","D1","D2","D3","D4","D5","D6","D7","D8","D9","DA","DB","DC","DD","DE","DF",
        "E0","E1","E2","E3","E4","E5","E6","E7","E8","E9","EA","EB","EC","ED","EE","EF",
        "F0","F1","F2","F3","F4","F5","F6","F7","F8","F9","FA","FB","FC","FD","FE","FF"
    };

    public static String byteToHex(byte b) {
        return hexes[b&0xFF];
    }

Ответ 17

Как и некоторые другие ответы, я рекомендую использовать String.format() и BigInteger. Но для интерпретации байтового массива как двоичного представления с прямым порядком байтов вместо двоичного представления с двумя дополнительными числами (с сигнумом и неполным использованием диапазона возможных шестнадцатеричных значений) используйте BigInteger (int signum, byte [] magnitude), а не BigInteger (byte [] val )

Например, для байтового массива длиной 8 используйте:

String.format("%016X", new BigInteger(1,bytes))

Преимущества:

  • ведущие нули
  • нет подписи
  • только встроенные функции
  • только одна строка кода

Недостаток:

  • Там могут быть более эффективные способы сделать это

Пример:

byte[] bytes = new byte[8];
Random r = new Random();
System.out.println("big-endian       | two's-complement");
System.out.println("-----------------|-----------------");
for (int i = 0; i < 10; i++) {
    r.nextBytes(bytes);
    System.out.print(String.format("%016X", new BigInteger(1,bytes)));
    System.out.print(" | ");
    System.out.print(String.format("%016X", new BigInteger(bytes)));
    System.out.println();
}

Пример вывода:

big-endian       | two's-complement
-----------------|-----------------
3971B56BC7C80590 | 3971B56BC7C80590
64D3C133C86CCBDC | 64D3C133C86CCBDC
B232EFD5BC40FA61 | -4DCD102A43BF059F
CD350CC7DF7C9731 | -32CAF338208368CF
82CDC9ECC1BC8EED | -7D3236133E437113
F438C8C34911A7F5 | -BC7373CB6EE580B
5E99738BE6ACE798 | 5E99738BE6ACE798
A565FE5CE43AA8DD | -5A9A01A31BC55723
032EBA783D2E9A9F | 032EBA783D2E9A9F
8FDAA07263217ABA | -70255F8D9CDE8546

Ответ 18

Короткий и простой способ конвертировать 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) для преобразования большого целого числа в шестнадцатеричную строку
  • Для разбора шестнадцатеричного числа используйте 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).

Обратное преобразование из hex в 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[] есть несколько ведущих нулей, они будут потеряны.

Ответ 19

Используйте

Integer.toHexString((int)b);