Код Java Для перетворення байту в шістнадцятковий


184

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

Чи є якась функція в Java для перетворення байтового масиву в шістнадцятковий?


2
Те, що ви називаєте байтовим масивом на Java, називається байтовим рядком іншими мовами (наприклад, docs.racket-lang.org/guide/bytestrings.html )
Патрік Фавре

Відповіді:


311
    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рішення більш переважним.

Список літератури


@Vivek: що таке "надзвичайно велика цінність"? Що таке вхід і який вихід?
полігенмастильні матеріали

Дозвольте пояснити ще раз. У мене є масив байтових рядків у масиві. Але мені потрібно зробити окремо проаналізувати кожен байт .. Отже, я не хочу працювати над усім масивом, але окремим рядом байтів за один раз, що є одним із компонентів цього масиву. Плутанина виникала через слово " масив ". Тепер у наведеному нижче коді "байт bv = 10; String hexString = Integer.toHexString (bv);" CAse 1 (Отриманий байт: 68 Hex Output:: 44) Випадок: 2 (Отриманий байт: -46 Hex Output:: ffffffd2) ......... Чому я отримую такий несподіваний результат для деяких значень?
Вівек

1
@Vivek: прочитайте мою відповідь про використання toHexString. Ви повинні замаскувати це & 0xFF, тобто Integer.toHexString(-46 & 0xFF)є "d2".
полігенмастильні матеріали

@polygenelubricants: Дякую багато .. Здається, нарешті код працює нормально. Чи безпечно використовувати функцію toHexString зараз? Або, можливо, будуть якісь лазівки з підходом?
Вівек

1
@Vivek: це "безпечно", ти просто повинен бути обережним і обов'язково маскуєш byteзначення & 0xFFкожного разу. formatрішення вище , можливо, потрібно буде маскування в залежності від того, що ви на самому справі , використовуючи в якості аргументу.
полігенмастильні матеріали

65

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

По-перше, що ми намагаємось зробити? Ми хочемо перетворити значення байта (або масив байтів) у рядок, який представляє шістнадцяткове значення в 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 (у двох доповненнях) у 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's complement)

2) Тепер перетворимо на Hex:

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(int i=0; i < bytes.length; i++){
    buffer.append(Character.forDigit((bytes[i] >> 4) & 0xF, 16));
    buffer.append(Character.forDigit((bytes[i] & 0xF), 16));
}

Сподіваємось, це пояснення робить речі зрозумілішими для тих, хто з вас цікавиться, що саме відбувається в багатьох прикладах, які ви знайдете в Інтернеті. Сподіваємось, я не допустив жодних кричущих помилок, але пропозиції та виправлення дуже вітаємо!


4
найкраща відповідь! Симетрична реалізація шістнадцяткових рядків у байт перетворювала б тоді використання Character.digit(), наприклад(byte) ((Character.digit(str.charAt(0), 16) << 4) + Character.digit(str.charAt(1), 16))
ericbn

21

Швидкий спосіб я ще не знайшов , щоб це зробити , полягає в наступному:

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

Це ~ 50x швидше 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);

2
DatatypeConverter більше не доступний на Java 9. Небезпечна річ - це код, який використовуватиметься, він буде компілюватися під Java 1.8 або раніше (Java 9 із початковими налаштуваннями раніше), але отримає виняток під час виконання програми під Java 9.
Stephen M -on strike-

Я другий пункт @StephenMs: використання цього з jre9 призведе до краху з винятком ClassNotFound
Патрік Фавре

Насправді можна просто витягнути метод вихідного коду printHexBinary з src.zip з jdk, який здається в 1 раз швидшим, ніж 1-й метод.
фрукти

1
Якщо ви працюєте з масивом char для постійної HEXES, замість String і charAt (), ви отримуєте ~ 20% більше швидкості.
Діоргіо

15

Спробуйте таким чином:

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()(оскільки вона справляється з негативними числами правильно).


2
Це підпише розширення, наприклад, спробуйте -1.
полігенмастильні матеріали

байт bv = 10; String hexString = Integer.toHexString (bv); Здається, це працює firne .. Я можу застосовувати його окремо на кожному елементі масиву. Інший код (Справа з масивом) надає занадто велике значення. Що може спричинити це?
Вівек

@Vivek, це тому, що в разі bvйого повернення одиничний шістнадцятковий символ . Тоді як решта коду повертає рядок шістнадцяткових символів . Я змінив код на деліметр, щоб ви могли його зараз зрозуміти.
0x2D9A3

@Bar: ви можете використовувати , Integer.toHexStringякщо ви маскувати byteз 0xFFдля розширення знака скасування.
полігенмастильні матеріали

CAse 1 (Отриманий байт: 68 Шістнадцятковий вихід:: 44) Випадок: 2 (Отриманий байт: -46 Шістнадцятковий вихід:: ffffffd2) Я отримую несподівані вихідні значення у випадку від'ємних байтових масивів ... Як це впоратися?
Вівек

13

Найкраще рішення - це односкладний гаманець:

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

як згадується тут


4
DatatypeConverter більше не доступний на Java 9. Небезпечна річ - це код, який використовуватиметься, він буде компілюватися під Java 1.8 або раніше (Java 9 із початковими налаштуваннями раніше), але отримає виняток під час виконання програми під Java 9.
Stephen M -on strike-

Сумно, коли ви говорите його не в jdk9. new BigInteger(byteArray).toString(16)тоді це шлях. Чи є проблеми з парфуми з цим ??
prayagupd

Можливо, не проблеми з парфумом, але вони пропустять провідні нулі (оскільки вони безглузді для такої кількості, як BigInteger)
Фрізо

Схоже, це все ще в java 9 docs , тому здається, що це нормально використовувати його все-таки з того, що я можу сказати
Brad Parks

Я думаю, як пояснено тут , все-таки добре використовувати для java9, але він буде видалений у майбутніх випусках Java. Ви також зможете використовувати його з "новим" автономним модулем jaxb, починаючи з версії 2.3.0 .
рись

11

Якщо ви хочете представити шістнадцяткове представлення постійної ширини, тобто 0Aзамість цього A, щоб ви могли однозначно відновити байти, спробуйте format():

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

11

Короткий і простий спосіб перетворення 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).

Перетворення назад із шістнадцяткового в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[]кількох провідних нулів вони будуть втрачені.


1
Якщо у провідного байта десяткове значення менше 16, ви також отримаєте рядок із непарною кількістю шістнадцяткових символів.
Алекс

8

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


4
Ще простішим способом є використання вбудованих функцій javax.xml.bind.DatatypeConverter / parseHexBinary та printHexBinary. Дивіться stackoverflow.com/questions/9655181/…
Алан Томпсон,

2
+1 до цього параметра. У Hex також є метод encodeHexString, який займає байт [] і повертає String.
Mingjiang Shi

не забувайте, що javaxпростір імен не завжди доступний.
Мене

7

Ви можете використовувати метод з бібліотеки постачальників замок Bouncy :

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

Крипто-пакет Bouncy Castle - це реалізація криптографічних алгоритмів на Java. Ця баночка містить постачальника послуг JCE та легкий API для API криптографії Bouncy Castle для JDK 1.5 до JDK 1.8.

Залежна залежність:

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

або від кодеку Apache Commons :

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

Пакет Codec Apache Commons містить простий кодер і декодери для різних форматів, таких як Base64 і Hexadecimal. На додаток до цих широко використовуваних кодерів і декодерів, пакет кодеків також підтримує колекцію фонетичних утиліт кодування.

Залежна залежність:

<dependency>
    <groupId>commons-codec</groupId>
    <artifactId>commons-codec</artifactId>
    <version>1.11</version>
</dependency>

Так, це найкраще рішення , але потрібна зовнішня бібліотека: Apache Commons Codec ( mvnrepository.com/artifact/commons-codec/commons-codec/1.11 ) або постачальник BouncyCastle ( mvnrepository.com/artifact/org.bouncycastle/bcprov- jdk15on / 1.60 )
Світлін Наков

5

Це код, за яким я виявив, що працює найшвидше. Я розмістив його на 109015 байтових масивах довжиною 32, за 23 мс. Я запускав його на VM, так що він, ймовірно, буде працювати швидше на голому металі.

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

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

Не працює: BigInteger(byteArrayOf(-1, 2, 3, 4, 5)).toString(16)повертається"-fdfcfbfb"
Мартін

Це правильний результат. Ви працюєте з байтами '-1', '2' ... '5'. У цих байтах немає візуалізації ( unicode.org ), якщо ви маєте намір працювати з буквальними '-1', '2' ... '5', вам слід працювати зі значеннями рядків.
Вендер

Це неправильний результат. Значення байта Java -1 - насправді 0xFF (це те саме, що (int) 255), оскільки байти Java підписані, тому результат повинен бути FF02030405. Якщо ви спробуєте рішення @Jerinaw вище, ви побачите, що воно надрукує правильний вихід. Також дивіться рішення Світліна Накова нижче.
Мартін

2

Ось проста функція перетворення байтів у шістнадцяткову

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

2

Інші висвітлювали загальну справу. Але якщо у вас є байтовий масив відомої форми, наприклад, 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]); 

1

Створення (і знищення) купки String екземплярів - це не гарний спосіб, якщо продуктивність є проблемою.

Будь ласка, ігноруйте ці багатослівні (дублікати) аргументи, що перевіряють твердження ( ifи). Це для (іншого) навчального призначення.

Повний проект 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);
}

1

Це дуже швидкий шлях. Зовнішні бібліотеки не потрібні.

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

1

Я не міг зрозуміти, що саме ти маєш на увазі під байтом 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

повернути ціле значення назад із змінної Byte:

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

Це вже пізно, але я сподіваюся, що це може допомогти деяким іншим;)


1

Є ваш швидкий метод:

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

1
потворний, але, ймовірно, дуже ефективний. :-)
Rich S

1

Як і деякі інші відповіді, я рекомендую використовувати String.format()і BigInteger. Але для інтерпретації масиву байтів як бінарного представлення big-endian замість двійкового подання з двома доповненнями (з signum та неповним використанням можливого діапазону шестигранних значень) використовуйте BigInteger (int signum, byte [] величина) , а не 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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.