Может кто-нибудь объяснить преобразование из байтового массива в шестнадцатеричную строку?

StackOverflow https://stackoverflow.com/questions/1043276

  •  20-08-2019
  •  | 
  •  

Вопрос

Недавно я начал изучать хеширование MD5 (в Java), и хотя я нашел алгоритмы и методы, которые помогут мне в этом, мне остается только удивляться, как это на самом деле работает.

Например, я нашел следующее в этом URL :

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

Я не нашел необходимости использовать битовое смещение в Java, поэтому я немного расстроен этим. Кто-то достаточно любезный, чтобы проиллюстрировать (простыми словами), как именно приведенный выше код выполняет преобразование? Quot &; GT &; GT &; GT &; Quot &;?

Я также нашел другие решения в StackOverflow, такие как здесь и здесь , который вместо этого использует BigInteger:

try {
   String s = "TEST STRING";
   MessageDigest md5 = MessageDigest.getInstance("MD5");
   md5.update(s.getBytes(),0,s.length());
   String signature = new BigInteger(1,md5.digest()).toString(16);
   System.out.println("Signature: "+signature);

} catch (final NoSuchAlgorithmException e) {
   e.printStackTrace();
}

Почему это тоже работает и какой путь более эффективен?

Спасибо за ваше время.

Это было полезно?

Решение

private static String convertToHex(byte[] data) {
    StringBuffer buf = new StringBuffer();
    for (int i = 0; i < data.length; i++) {

До этого момента ... просто базовая настройка и запуск цикла для прохождения всех байтов в массиве

        int halfbyte = (data[i] >>> 4) & 0x0F;

байты при преобразовании в шестнадцатеричный формат - это две шестнадцатеричные цифры или 8 двоичных цифр, в зависимости от того, на какой базе вы на это смотрите. ; - беззнаковое смещение вправо) и логическое И его с 0000 1111, так что результатом является целое число, равное старшим 4 битам байта (первая шестнадцатеричная цифра).

Скажем, 23 был вход, это 0001 0111 в двоичном виде. Сдвиг делает и логическое И закрывает это до 0000 0001.

        int two_halfs = 0;
        do {

Это просто настраивает цикл do / while на двойной запуск

            if ((0 <= halfbyte) && (halfbyte <= 9))
                buf.append((char) ('0' + halfbyte));
            else
                buf.append((char) ('a' + (halfbyte - 10)));

Здесь мы показываем фактическую шестнадцатеричную цифру, в основном просто используя ноль или символ в качестве начальной точки и перемещаясь к правильному символу. Первый оператор if покрывает все цифры 0-9, а второй - все цифры 10-15 (a-f в шестнадцатеричном формате)

Опять же, в нашем примере 0000 0001 в десятичном формате равен 1. Мы попадаем в верхний блок if и добавляем 1 к символу '0', чтобы получить символ '1', добавляем его в строку и продолжаем .

                halfbyte = data[i] & 0x0F;

Теперь мы установили целое число так, чтобы оно равнялось младшим битам байта и повторялось.

Опять же, если наш ввод был 23 ... 0001 0111 после логического И становится просто 0000 0111, который является 7 в десятичном виде. Повторите ту же логику, что и выше, и появится символ «7».

            } while(two_halfs++ < 1);

Теперь мы просто переходим к следующему байту в массиве и повторяем.

        }
    return buf.toString();
}

Чтобы ответить на ваш следующий вопрос, Java API уже имеет базовую утилиту преобразования, уже встроенную в BigInteger. См. toString (int radix ) документация.

Не зная реализации, используемой Java API, я не могу сказать наверняка, но я готов поспорить, что расширение Java более эффективно, чем первый несколько простой алгоритм, который вы опубликовали.

Другие советы

Чтобы ответить на этот бит:

  

Почему это тоже работает

Это не так. По крайней мере, не так, как в версии цикла. new BigInteger (...). toString (16) не будет показывать начальные нули, как в предыдущей версии. Обычно для чего-то вроде записи байтового массива (особенно такого, который представляет что-то вроде хэша), вы хотите получить вывод фиксированной длины, поэтому, если вы хотите использовать эту версию, вам нужно соответствующим образом дополнить ее.

Для подробного объяснения битшифтинга ознакомьтесь с ответами на следующий вопрос SO Что такое операторы побитового сдвига (битового сдвига) и как они работают?

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

  if ((0 <= halfbyte) && (halfbyte <= 9))
                buf.append((char) ('0' + halfbyte));
            else
                buf.append((char) ('a' + (halfbyte - 10)));

Это упрощенный ответ, но я все равно не такой яркий = D

Эти вещи вам не нужно писать самостоятельно, потому что они уже написаны на apache-commons-codec:

import org.apache.commons.codec.binary.Hex;
...
Hex.encodeHexString(byte[] array)

В Hex классе есть много других полезных методов.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top