У меня есть массив байтов, заполненный шестнадцатеричными числами, и его легко печатать легко, потому что есть много непечатаемых элементов. Мне нужен именно точный hexcode в виде: 3a5f771c
Ответ 12
Вот несколько общих опций, упорядоченных от простого (однострочный) до сложного (огромная библиотека).
Вариант 1. Фрагмент кода - Простой
Одно очень простое решение - использовать шестнадцатеричное представление BigInteger
:
new BigInteger(1, someByteArray).toString(16)
Обратите внимание, что поскольку он обрабатывает числа, а не произвольные строки байтов, он пропускает начальные нули - это может или не может быть тем, что вы хотите (например, 000AE3
против 0AE3
). Это также очень медленно, примерно в 50 раз медленнее по сравнению со следующим вариантом.
Вариант 2. Фрагмент кода - расширенный
Вот полнофункциональный фрагмент кода, поддерживающий верхний/нижний регистр и порядковый номер. Он оптимизирован для минимизации сложности памяти и максимальной производительности и должен быть совместим со всеми современными версиями Java (5+).
private static final char[] LOOKUP_TABLE_LOWER = new char[]{0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66};
private static final char[] LOOKUP_TABLE_UPPER = new char[]{0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46};
public static String encode(byte[] byteArray, boolean upperCase, ByteOrder byteOrder) {
// our output size will be exactly 2x byte-array length
final char[] buffer = new char[byteArray.length * 2];
// choose lower or uppercase lookup table
final char[] lookup = upperCase ? LOOKUP_TABLE_UPPER : LOOKUP_TABLE_LOWER;
int index;
for (int i = 0; i < byteArray.length; i++) {
// for little endian we count from last to first
index = (byteOrder == ByteOrder.BIG_ENDIAN) ? i : byteArray.length - i - 1;
// extract the upper 4 bit and look up char (0-A)
buffer[i << 1] = lookup[(byteArray[index] >> 4) & 0xF];
// extract the lower 4 bit and look up char (0-A)
buffer[(i << 1) + 1] = lookup[(byteArray[index] & 0xF)];
}
return new String(buffer);
}
public static String encode(byte[] byteArray) {
return encode(byteArray, false, ByteOrder.BIG_ENDIAN);
}
По сути, он использует код из варианта 3. Если вы хотите увидеть микро-тест JMH, сравнивающий несколько реализаций, см. здесь. Полный исходный код с лицензией и декодером Apache v2 можно найти здесь.
Вариант 3. Использование небольшой оптимизированной библиотеки: bytes-java
Работая над моим предыдущим проектом, я создал этот небольшой инструментарий для работы с байтами в Java. Он не имеет внешних зависимостей и совместим с Java 7+. Он включает, среди прочего, очень быстрый и хорошо протестированный HEX en/decoder:
import at.favre.lib.bytes.Bytes;
...
Bytes.wrap(someByteArray).encodeHex()
Вы можете проверить это на Github: bytes-java.
Вариант 4. Кодек Apache Commons
Конечно, есть хорошие общие кодеки. (предупреждающее мнение впереди) Во время работы над проектом, описанным выше, я проанализировал код и был весьма разочарован; множество дублированных неорганизованных кодов, устаревших и экзотических кодеков, вероятно, полезно только для очень немногих и довольно изощренных и медленных реализаций популярных кодеков (особенно Base64). Поэтому я бы принял обоснованное решение, если вы хотите использовать его или альтернативу. В любом случае, если вы все еще хотите его использовать, вот фрагмент кода:
import org.apache.commons.codec.binary.Hex;
...
Hex.encodeHexString(someByteArray));
Вариант 5: Google Guava
Чаще всего у вас уже есть Guava в качестве зависимости. Если это так, просто используйте:
import com.google.common.io.BaseEncoding;
...
BaseEncoding.base16().lowerCase().encode(someByteArray);
Вариант 6: Spring Security
Если вы используете Spring Framework с Spring Security, вы можете использовать следующее:
import org.springframework.security.crypto.codec.Hex
...
Hex.encode(someByteArray);
Совместимость с Java 9 или "Не используйте JAXBs javax/xml/bind/DatatypeConverter"
В предыдущих версиях Java (8 и ниже) код Java для JAXB был включен как зависимость времени выполнения. Начиная с Java 9 и Jigsaw modularisation ваш код не может получить доступ к другому коду вне его модуля. Так что будьте внимательны, если вы получите исключение, например:
java.lang.NoClassDefFoundError: javax/xml/bind/JAXBException
при работе на JVM с Java 9+. Если это так, то переключите реализации на любую из альтернатив, описанных выше. Смотрите также этот вопрос.
Ответ 17
Добавление утилиты jar для простой функции не является хорошим вариантом. Вместо этого соберите свои собственные служебные классы. Следующее возможно быстрее реализации.
public class ByteHex {
public static int hexToByte(char ch) {
if ('0' <= ch && ch <= '9') return ch - '0';
if ('A' <= ch && ch <= 'F') return ch - 'A' + 10;
if ('a' <= ch && ch <= 'f') return ch - 'a' + 10;
return -1;
}
private static final String[] byteToHexTable = 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"
};
private static final String[] byteToHexTableLowerCase = 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 byteToHexTable[b & 0xFF];
}
public static String byteToHex(byte[] bytes){
if(bytes == null) return null;
StringBuilder sb = new StringBuilder(bytes.length*2);
for(byte b : bytes) sb.append(byteToHexTable[b & 0xFF]);
return sb.toString();
}
public static String byteToHex(short[] bytes){
StringBuilder sb = new StringBuilder(bytes.length*2);
for(short b : bytes) sb.append(byteToHexTable[((byte)b) & 0xFF]);
return sb.toString();
}
public static String byteToHexLowerCase(byte[] bytes){
StringBuilder sb = new StringBuilder(bytes.length*2);
for(byte b : bytes) sb.append(byteToHexTableLowerCase[b & 0xFF]);
return sb.toString();
}
public static byte[] hexToByte(String hexString) {
if(hexString == null) return null;
byte[] byteArray = new byte[hexString.length() / 2];
for (int i = 0; i < hexString.length(); i += 2) {
byteArray[i / 2] = (byte) (hexToByte(hexString.charAt(i)) * 16 + hexToByte(hexString.charAt(i+1)));
}
return byteArray;
}
public static byte hexPairToByte(char ch1, char ch2) {
return (byte) (hexToByte(ch1) * 16 + hexToByte(ch2));
}
}