Может кто-нибудь объяснить преобразование из байтового массива в шестнадцатеричную строку?
Вопрос
Недавно я начал изучать хеширование 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
классе есть много других полезных методов.