В Java, как я могу преобразовать байтовый массив в строку шестнадцатеричных цифр, сохраняя ведущие нули? - PullRequest
156 голосов
/ 01 декабря 2008

Я работаю с примером кода Java для создания хэшей md5. Одна часть преобразует результаты из байтов в строку шестнадцатеричных цифр:

byte messageDigest[] = algorithm.digest();     
StringBuffer hexString = new StringBuffer();
for (int i=0;i<messageDigest.length;i++) {
    hexString.append(Integer.toHexString(0xFF & messageDigest[i]));
    }

Однако, это не совсем работает, поскольку toHexString, по-видимому, сбрасывает начальные нули. Итак, как проще всего перейти от байтового массива к шестнадцатеричной строке, которая поддерживает ведущие нули?

Ответы [ 28 ]

126 голосов
/ 02 декабря 2008

Проверьте кодек Apache Commons Hex.encodeHex . Тип возврата - char[], который можно легко преобразовать в String. Итак:

import org.apache.commons.codec.binary;

Hex.encodeHexString(messageDigest);
109 голосов
/ 03 июня 2009

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

public static String toHex(byte[] bytes) {
    BigInteger bi = new BigInteger(1, bytes);
    return String.format("%0" + (bytes.length << 1) + "X", bi);
}

Если вы хотите использовать строчные шестнадцатеричные цифры, используйте "x" в формате String.

98 голосов
/ 01 декабря 2008

Простой подход состоит в том, чтобы проверить, сколько цифр выводится на Integer.toHexString(), и, если необходимо, добавить начальный ноль к каждому байту. Как то так:

public static String toHexString(byte[] bytes) {
    StringBuilder hexString = new StringBuilder();

    for (int i = 0; i < bytes.length; i++) {
        String hex = Integer.toHexString(0xFF & bytes[i]);
        if (hex.length() == 1) {
            hexString.append('0');
        }
        hexString.append(hex);
    }

    return hexString.toString();
}
38 голосов
/ 28 января 2013

Используйте DatatypeConverter.printHexBinary(). Вы можете прочитать его документацию в http://docs.oracle.com/javase/6/docs/api/javax/xml/bind/DatatypeConverter.html

Например:

byte bytes[] = {(byte)0, (byte)0, (byte)134, (byte)0, (byte)61};
System.out.println(javax.xml.bind.DatatypeConverter.printHexBinary(bytes));

Результатом будет:

000086003D
32 голосов
/ 04 февраля 2010

Мне понравились представления Стива, но он мог обойтись без пары переменных и сохранить несколько строк в процессе.

public static String toHexString(byte[] bytes) {
    char[] hexArray = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
    char[] hexChars = new char[bytes.length * 2];
    int v;
    for ( int j = 0; j < bytes.length; j++ ) {
        v = bytes[j] & 0xFF;
        hexChars[j*2] = hexArray[v/16];
        hexChars[j*2 + 1] = hexArray[v%16];
    }
    return new String(hexChars);
}

Что мне нравится в этом, так это то, что легко увидеть, что именно он делает (вместо того, чтобы полагаться на какое-то волшебное преобразование черного ящика BigInteger), и вам также не нужно беспокоиться о угловых случаях, таких как начальные нули и тому подобное. Эта процедура берет каждый 4-битный кусок и превращает его в шестнадцатеричный символ. И он использует поиск по таблице, так что это, вероятно, быстро. Возможно, это будет быстрее, если вы замените v / 16 и v% 16 битовыми сдвигами и AND, но мне сейчас лень проверять это.

21 голосов
/ 15 июня 2009

Я обнаружил, что Integer.toHexString немного медленный. Если вы конвертируете много байтов, вы можете рассмотреть возможность создания массива строк, содержащих «00» .. «FF» и использования целого числа в качестве индекса. * 1001 Т.е. *

hexString.append(hexArray[0xFF & messageDigest[i]]);

Это быстрее и обеспечивает правильную длину. Просто требуется массив строк:

String[] hexArray = {
"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"};
13 голосов
/ 26 июня 2009

Я искал то же самое ... несколько хороших идей здесь, но я провел несколько микро-тестов. Я обнаружил, что следующее является самым быстрым (изменено по сравнению с Айманом выше и примерно в 2 раза быстрее, и примерно на 50% быстрее, чем Стив чуть выше этого):

public static String hash(String text, String algorithm)
        throws NoSuchAlgorithmException {
    byte[] hash = MessageDigest.getInstance(algorithm).digest(text.getBytes());
    return new BigInteger(1, hash).toString(16);
}

Редактировать: Упс - пропустил, что это, по сути, то же самое, что и kgiannakakis, и поэтому может лишить лидирующие 0. Тем не менее, если изменить это на следующее, он все еще самый быстрый:

public static String hash(String text, String algorithm)
        throws NoSuchAlgorithmException {
    byte[] hash = MessageDigest.getInstance(algorithm).digest(text.getBytes());
    BigInteger bi = new BigInteger(1, hash);
    String result = bi.toString(16);
    if (result.length() % 2 != 0) {
        return "0" + result;
    }
    return result;
}
11 голосов
/ 14 апреля 2010
static String toHex(byte[] digest) {
    StringBuilder sb = new StringBuilder();
    for (byte b : digest) {
        sb.append(String.format("%1$02X", b));
    }

    return sb.toString();
}
6 голосов
/ 01 декабря 2008
String result = String.format("%0" + messageDigest.length + "s", hexString.toString())

Это самое короткое решение с учетом того, что у вас уже есть. Если вы можете преобразовать байтовый массив в числовое значение, String.format может преобразовать его в шестнадцатеричную строку одновременно.

5 голосов
/ 22 января 2015

Гуава тоже довольно просто:

BaseEncoding.base16().encode( bytes );

Это хорошая альтернатива, когда Apache Commons недоступен. У этого также есть некоторые хорошие средства управления выводом как:

byte[] bytes = new byte[] { 0xa, 0xb, 0xc, 0xd, 0xe, 0xf };
BaseEncoding.base16().lowerCase().withSeparator( ":", 2 ).encode( bytes );
// "0a:0b:0c:0d:0e:0f"
...