Как преобразовать байтовый массив в шестнадцатеричную строку в Java?
У меня есть байтовый массив, заполненный шестнадцатеричными числами, и печатать его простым способом довольно бессмысленно, потому что там много непечатаемых элементов. Что мне нужно, так это точный шестнадцатеричный код в виде: 3a5f771c
35 ответов
Из обсуждения здесь, и особенно из этого ответа, это функция, которую я сейчас использую:
private final static char[] hexArray = "0123456789ABCDEF".toCharArray();
public static String bytesToHex(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);
}
Мои собственные крошечные тесты (миллион байтов в тысячу раз, 256 байтов в 10 миллионов раз) показали, что он намного быстрее, чем любая другая альтернатива, примерно вдвое меньше для длинных массивов. По сравнению с ответом, который я получил, переключение на побитовые операции - как предложено в обсуждении - сократило время на длинные массивы примерно на 20%. (Изменить: когда я говорю, что это быстрее, чем альтернативы, я имею в виду альтернативный код, предложенный в обсуждениях. Производительность эквивалентна кодеку Commons, который использует очень похожий код.)
В библиотеке кодеков Apache Commons есть класс Hex для выполнения именно этого типа работы.
import org.apache.commons.codec.binary.Hex;
String foo = "I am a string";
byte[] bytes = foo.getBytes();
System.out.println( Hex.encodeHexString( bytes ) );
Использование 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
Как вы можете видеть, это будет извлекать шестнадцатеричную строку, представляющую массив байтов с ведущими нулями.
Этот ответ в основном такой же, как и в вопросе В Java, как преобразовать байтовый массив в строку шестнадцатеричных цифр, сохраняя начальные нули?
Самое простое решение, без внешних библиотек, без констант цифр:
public static String byteArrayToHex(byte[] a) {
StringBuilder sb = new StringBuilder(a.length * 2);
for(byte b: a)
sb.append(String.format("%02x", b));
return sb.toString();
}
Вот несколько распространенных вариантов, от простых (однострочных) до сложных (огромная библиотека). Если вас интересует производительность, см. Микротесты ниже.
Вариант 1. Фрагмент кода - простой (только с использованием JDK/Android)
Вариант 1а: BigInteger
Одно очень простое решение - использовать BigInteger
шестнадцатеричное представление:
new BigInteger(1, someByteArray).toString(16);
Обратите внимание, что, поскольку это обрабатывает числа, а не произвольные байтовые строки, начальные нули будут опущены - это может быть, а может и не быть тем, что вам нужно (например,000AE3
против 0AE3
для 3-байтового входа). Это тоже очень медленно, примерно в 100 раз медленнее по сравнению с вариантом 2.
Вариант 1b: String.format()
Используя %X
заполнитель String.format()
умеет кодировать большинство примитивных типов (short
, int
, long
) в шестнадцатеричный:
String.format("%X", ByteBuffer.wrap(eightByteArray).getLong());
Вариант 1c: целое число / длинное число (только массивы 4/8 байтов)
Если у вас есть исключительно 4-байтовые массивы, вы можете использовать toHexString
метод класса Integer:
Integer.toHexString(ByteBuffer.wrap(fourByteArray).getInt());
То же самое работает с 8-байтовыми массивами и Long
Long.toHexString(ByteBuffer.wrap(eightByteArray).getLong());
Вариант 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);
}
Полный исходный код с лицензией Apache v2 и декодером можно найти здесь.
Вариант 3: Использование небольшой оптимизированной библиотеки: bytes-java
Работая над своим предыдущим проектом, я создал этот небольшой набор инструментов для работы с байтами в Java. Он не имеет внешних зависимостей и совместим с Java 7+. Он включает, среди прочего, очень быстрый и хорошо протестированный HEX en/ декодер:
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 с Spring Security, вы можете использовать следующее:
import org.springframework.security.crypto.codec.Hex
...
new String(Hex.encode(someByteArray));
Вариант 7: Надувной замок
Если вы уже используете фреймворк безопасности Bouncy Castle, вы можете использовать егоHex
util:
import org.bouncycastle.util.encoders.Hex;
...
Hex.toHexString(someByteArray);
Не совсем вариант 8: Совместимость с Java 9+ или "Не использовать JAXB javax/xml/bind/DatatypeConverter"
В предыдущих версиях Java (8 и ниже) код Java для JAXB был включен как зависимость времени выполнения. Поскольку Java 9 и Jigsaw модульны, ваш код не может получить доступ к другому коду вне своего модуля без явного объявления. Так что имейте в виду, если вы получите исключение, например:
java.lang.NoClassDefFoundError: javax/xml/bind/JAXBException
при работе на JVM с Java 9+. Если это так, переключитесь на любую из вышеперечисленных реализаций. См. Также этот вопрос.
Микро тесты
Вот результаты простого теста JMH Micro для кодирования массивов байтов разного размера. Значения представляют собой операции в секунду, поэтому чем выше, тем лучше. Обратите внимание, что микротесты очень часто не отражают поведение в реальном мире, поэтому относитесь к этим результатам с недоверием.
| Name (ops/s) | 16 byte | 32 byte | 128 byte | 0.95 MB |
|----------------------|-----------:|-----------:|----------:|--------:|
| Opt1: BigInteger | 2,088,514 | 1,008,357 | 133,665 | 4 |
| Opt2/3: Bytes Lib | 20,423,170 | 16,049,841 | 6,685,522 | 825 |
| Opt4: Apache Commons | 17,503,857 | 12,382,018 | 4,319,898 | 529 |
| Opt5: Guava | 10,177,925 | 6,937,833 | 2,094,658 | 257 |
| Opt6: Spring | 18,704,986 | 13,643,374 | 4,904,805 | 601 |
| Opt7: BC | 7,501,666 | 3,674,422 | 1,077,236 | 152 |
| Opt8: JAX-B | 13,497,736 | 8,312,834 | 2,590,940 | 346 |
Технические характеристики: JDK 8u202, i7-7700K, Win10, оперативная память 24 ГБ. См. Полный тест здесь.
Решение Guava, для полноты:
import com.google.common.io.BaseEncoding;
...
byte[] bytes = "Hello world".getBytes(StandardCharsets.UTF_8);
final String hex = BaseEncoding.base16().lowerCase().encode(bytes);
Сейчас hex
является "48656c6c6f20776f726c64"
,
Этот простой oneliner работает для меняString result = new BigInteger(1, inputBytes).toString(16);
РЕДАКТИРОВАТЬ - Использование этого удалит ведущие нули, но они работали для моего варианта использования. Спасибо @Voicu за указание на это
Наконец, Java 17 содержит класс HexFormat, поэтому вы можете просто:
HexFormat.of().formatHex(bytes);
Он поддерживает конфигурацию в нижнем / верхнем регистре, разделителях, префиксе, суффиксе и т. Д.
Я бы использовал что-то вроде этого для фиксированной длины, например, хэши:
md5sum = String.format("%032x", new BigInteger(1, md.digest()));
Используйте класс DataTypeConverterjavax.xml.bind.DataTypeConverter
String hexString = DatatypeConverter.printHexBinary(bytes[] raw);
Я нашел три разных способа здесь: http://www.rgagnon.com/javadetails/java-0596.html
Самым элегантным, как он также отмечает, я думаю, является этот:
static final String HEXES = "0123456789ABCDEF";
public static String getHex( byte [] raw ) {
if ( raw == null ) {
return null;
}
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();
}
При незначительной стоимости хранения таблицы соответствия эта реализация проста и очень быстра.
private static final char[] BYTE2HEX=(
"000102030405060708090A0B0C0D0E0F"+
"101112131415161718191A1B1C1D1E1F"+
"202122232425262728292A2B2C2D2E2F"+
"303132333435363738393A3B3C3D3E3F"+
"404142434445464748494A4B4C4D4E4F"+
"505152535455565758595A5B5C5D5E5F"+
"606162636465666768696A6B6C6D6E6F"+
"707172737475767778797A7B7C7D7E7F"+
"808182838485868788898A8B8C8D8E8F"+
"909192939495969798999A9B9C9D9E9F"+
"A0A1A2A3A4A5A6A7A8A9AAABACADAEAF"+
"B0B1B2B3B4B5B6B7B8B9BABBBCBDBEBF"+
"C0C1C2C3C4C5C6C7C8C9CACBCCCDCECF"+
"D0D1D2D3D4D5D6D7D8D9DADBDCDDDEDF"+
"E0E1E2E3E4E5E6E7E8E9EAEBECEDEEEF"+
"F0F1F2F3F4F5F6F7F8F9FAFBFCFDFEFF").toCharArray();
;
public static String getHexString(byte[] bytes) {
final int len=bytes.length;
final char[] chars=new char[len<<1];
int hexIndex;
int idx=0;
int ofs=0;
while (ofs<len) {
hexIndex=(bytes[ofs++] & 0xFF)<<1;
chars[idx++]=BYTE2HEX[hexIndex++];
chars[idx++]=BYTE2HEX[hexIndex];
}
return new String(chars);
}
HexFormat был добавлен в Java 17:
String hex = HexFormat.of().formatHex(array);
Нам не нужно использовать какую-либо внешнюю библиотеку или писать код на основе циклов и констант.
Достаточно только этого:
byte[] theValue = .....
String hexaString = new BigInteger(1, theValue).toString(16);
Как насчет этого?
String byteToHex(final byte[] hash)
{
Formatter formatter = new Formatter();
for (byte b : hash)
{
formatter.format("%02x", b);
}
String result = formatter.toString();
formatter.close();
return result;
}
Вот еще один метод с использованием Streams:
private static String toHexString(byte[] bytes) {
return IntStream.range(0, bytes.length)
.mapToObj(i -> String.format("%02X", bytes[i]))
.collect(Collectors.joining());
}
Добавление утилиты 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));
}
}
public static String toHexString(byte[] bytes) {
StringBuilder sb = new StringBuilder();
if (bytes != null)
for (byte b:bytes) {
final String hexString = Integer.toHexString(b & 0xff);
if(hexString.length()==1)
sb.append('0');
sb.append(hexString);//.append(' ');
}
return sb.toString();//.toUpperCase();
}
Чтобы использовать DatatypeConverter:
public String toHexString(byte... bytes) {
return Optional.ofNullable(bytes)
.filter(bs->bs.length>0)
.map(DatatypeConverter::printHexBinary)
.map(str->IntStream.range(0, str.length())
.filter(i->(i%2)==0) // take every second index
.mapToObj(i->"0x" + str.substring(i, i+2))
.collect(Collectors.joining(" ")))
.orElse("");
}
Если вы используете платформу Spring Security, вы можете использовать:
import org.springframework.security.crypto.codec.Hex
final String testString = "Test String";
final byte[] byteArray = testString.getBytes();
System.out.println(Hex.encode(byteArray));
Я обычно использую следующий метод для заявления debuf, но я не знаю, является ли это лучшим способом сделать это или нет
private static String digits = "0123456789abcdef";
public static String toHex(byte[] data){
StringBuffer buf = new StringBuffer();
for (int i = 0; i != data.length; i++)
{
int v = data[i] & 0xff;
buf.append(digits.charAt(v >> 4));
buf.append(digits.charAt(v & 0xf));
}
return buf.toString();
}
Итак, есть несколько способов сделать это, но если вы решите использовать библиотеку, я бы посоветовал покопаться в вашем проекте, чтобы увидеть, было ли что-то реализовано в библиотеке, которая уже является частью вашего проекта, прежде чем добавлять новую библиотеку. просто чтобы сделать это. Например, если у вас еще нет
org.apache.commons.codec.binary.Hex
может быть, у вас есть...
org.apache.xerces.impl.dv.util.HexBin
Недавно мне пришлось реализовать шестнадцатеричный преобразователь, чтобы выгружать байтовый поток в журнал в шестнадцатеричном формате. Изначально я делал это с помощью
Hex.encodeHex
который уже обсуждался здесь.
Но если вы хотите представить массив байтов в очень презентабельном / читаемом виде, библиотека может быть отличным применением, поскольку она распечатывает шестнадцатеричный код, а также строки в нем, устраняя непечатаемые символы.
Требование было примерно таким,
0010 56 56 09 35 32 f0 b2 00 50 4c 45 41 53 45 20 52 VV.52...PLEASE R
0020 45 2d 45 4e 54 45 52 20 4c 41 53 54 20 54 52 41 E-ENTER LAST TRA
0030 4e 53 41 43 54 49 4f 4e 00 04 NSACTION..
Самый короткий способ сделать то же самое в более презентабельном виде с помощью
io.netty.buffer
является
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufUtil;
import io.netty.buffer.Unpooled;
void hexDump(byte[] buf) {
ByteBuf byteBuf = Unpooled.wrappedBuffer(buf);
log.trace("Bytes received (Hex)\n" + ByteBufUtil.prettyHexDump(byteBuf.slice()));
}
если вы используете maven, включите приведенную ниже зависимость в pom.xml (проверьте последнюю версию на странице netty)
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-buffer</artifactId>
<version>4.1.68.Final</version>
</dependency>
вывод был:
+-------------------------------------------------+
| 0 1 2 3 4 5 6 7 8 9 a b c d e f |
+--------+-------------------------------------------------+----------------+
|00000010| 40 40 b3 f3 80 f3 80 f3 80 f1 48 f1 41 f1 4e f1 |@@........H.A.N.|
|00000020| 47 f1 49 f1 4e f1 47 b5 f1 52 f1 4f f1 43 f1 4b |G.I.N.G..R.O.C.K|
|00000030| f3 80 f3 80 41 b4 40 40 f3 80 f3 80 40 f3 80 04 |....A.@@....@...|
+--------+-------------------------------------------------+----------------+
Для справки, долгий путь (возможно, не самый эффективный) с использованием обсуждаемых в ответах методов:
public static String hexDump(byte[] buf) throws DecoderException
{
ByteBuffer byteBuf = ByteBuffer.wrap(buf);
char[] result = Hex.encodeHex(byteBuf);
String bin = new String(result).toUpperCase();
String str = new String(Hex.decodeHex(bin), StandardCharsets.UTF_8);
str = str.replaceAll("[^!-~]", ".");
StringBuilder out = new StringBuilder();
int bytes_per_line = 16;
for (int pos = 0; pos < str.length(); pos += bytes_per_line) {
out.append(String.format("%04X ", pos));
if (2 * (pos + bytes_per_line) >= bin.length()) {
out.append(String.format("%-" + 2 * bytes_per_line + "s", bin.substring(2 * pos)).replaceAll("..", "$0 "));
} else {
out.append(bin.substring(2 * pos, 2 * (pos + bytes_per_line)).replaceAll("..", "$0 "));
}
out.append(" ");
if (pos + bytes_per_line > str.length()) {
out.append(str.substring(pos));
} else {
out.append(str.substring(pos, pos + bytes_per_line));
}
out.append("\n");
}
return out.toString();
}
Я предпочитаю использовать это:
final protected static char[] hexArray = "0123456789ABCDEF".toCharArray();
public static String bytesToHex(byte[] bytes, int offset, int count) {
char[] hexChars = new char[count * 2];
for ( int j = 0; j < count; j++ ) {
int v = bytes[j+offset] & 0xFF;
hexChars[j * 2] = hexArray[v >>> 4];
hexChars[j * 2 + 1] = hexArray[v & 0x0F];
}
return new String(hexChars);
}
Это несколько более гибкая адаптация принятого ответа. Лично я сохраняю принятый ответ и эту перегрузку вместе с ним, чтобы его можно было использовать в более широком контексте.
Вот java.util.Base64
реализация (частичная), не правда ли?
public class Base16/*a.k.a. Hex*/ {
public static class Encoder{
private static char[] toLowerHex={'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
private static char[] toUpperHex={'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
private boolean upper;
public Encoder(boolean upper) {
this.upper=upper;
}
public String encode(byte[] data){
char[] value=new char[data.length*2];
char[] toHex=upper?toUpperHex:toLowerHex;
for(int i=0,j=0;i<data.length;i++){
int octet=data[i]&0xFF;
value[j++]=toHex[octet>>4];
value[j++]=toHex[octet&0xF];
}
return new String(value);
}
static final Encoder LOWER=new Encoder(false);
static final Encoder UPPER=new Encoder(true);
}
public static Encoder getEncoder(){
return Encoder.LOWER;
}
public static Encoder getUpperEncoder(){
return Encoder.UPPER;
}
//...
}
Если вы хотите сделать его более читаемым и отделить байты друг от друга, вы можете использовать следующий код в Java 17+:
byte[] yourByteArray = { -128, 0, 127 };
String hexString = new String(HexFormat.ofDelimiter(" ").formatHex(yourByteArray));
// 80 00 7f
Мое решение основано на решении возможно WeCouldStealAVan, но не опирается на дополнительные таблицы поиска. Он не использует взломы типа int-to-char (на самом деле, Character.forDigit()
делает это, выполняя некоторое сравнение, чтобы проверить, что на самом деле является цифра) и, следовательно, может быть немного медленнее. Пожалуйста, не стесняйтесь использовать его где угодно. Приветствия.
public static String bytesToHex(final byte[] bytes)
{
final int numBytes = bytes.length;
final char[] container = new char[numBytes * 2];
for (int i = 0; i < numBytes; i++)
{
final int b = bytes[i] & 0xFF;
container[i * 2] = Character.forDigit(b >>> 4, 0x10);
container[i * 2 + 1] = Character.forDigit(b & 0xF, 0x10);
}
return new String(container);
}
Небольшой вариант решения, предложенного @maybewecouldstealavan, который позволяет визуально объединять N байтов в выходную шестнадцатеричную строку:
final static char[] HEX_ARRAY = "0123456789ABCDEF".toCharArray();
final static char BUNDLE_SEP = ' ';
public static String bytesToHexString(byte[] bytes, int bundleSize /*[bytes]*/]) {
char[] hexChars = new char[(bytes.length * 2) + (bytes.length / bundleSize)];
for (int j = 0, k = 1; j < bytes.length; j++, k++) {
int v = bytes[j] & 0xFF;
int start = (j * 2) + j/bundleSize;
hexChars[start] = HEX_ARRAY[v >>> 4];
hexChars[start + 1] = HEX_ARRAY[v & 0x0F];
if ((k % bundleSize) == 0) {
hexChars[start + 2] = BUNDLE_SEP;
}
}
return new String(hexChars).trim();
}
То есть:
bytesToHexString("..DOOM..".toCharArray().getBytes(), 2);
2E2E 444F 4F4D 2E2E
bytesToHexString("..DOOM..".toCharArray().getBytes(), 4);
2E2E444F 4F4D2E2E
На этой странице не найдено ни одного решения, которое бы не
- Используйте цикл
- Используйте javax.xml.bind.DatatypeConverter, который прекрасно компилируется, но часто выдает java.lang.NoClassDefFoundError во время выполнения.
Вот решение, в котором нет недостатков, описанных выше (хотя никаких обещаний у меня нет и других недостатков)
import java.math.BigInteger;
import static java.lang.System.out;
public final class App2 {
// | proposed solution.
public static String encode(byte[] bytes) {
final int length = bytes.length;
// | BigInteger constructor throws if it is given an empty array.
if (length == 0) {
return "00";
}
final int evenLength = (int)(2 * Math.ceil(length / 2.0));
final String format = "%0" + evenLength + "x";
final String result = String.format (format, new BigInteger(bytes));
return result;
}
public static void main(String[] args) throws Exception {
// 00
out.println(encode(new byte[] {}));
// 01
out.println(encode(new byte[] {1}));
//203040
out.println(encode(new byte[] {0x20, 0x30, 0x40}));
// 416c6c20796f75722062617365206172652062656c6f6e6720746f2075732e
out.println(encode("All your base are belong to us.".getBytes()));
}
}
Я не мог получить это под 62 кодами операций, но если вы можете жить без заполнения 0 в случае, если первый байт меньше 0x10, то в следующем решении используются только 23 кода операции. Действительно показывает, как "легко реализовать себя" такие решения, как "pad с нулем, если длина строки нечетная", могут стать довольно дорогими, если собственная реализация еще не доступна (или в этом случае, если BigInteger имел опцию префикса с нулями в нанизывать).
public static String encode(byte[] bytes) {
final int length = bytes.length;
// | BigInteger constructor throws if it is given an empty array.
if (length == 0) {
return "00";
}
return new BigInteger(bytes).toString(16);
}
Converts bytes data to hex characters
@param bytes byte array to be converted to hex string
@return byte String in hex format
private static String bytesToHex(byte[] bytes) {
char[] hexChars = new char[bytes.length * 2];
int v;
for (int j = 0; j < bytes.length; j++) {
v = bytes[j] & 0xFF;
hexChars[j * 2] = HEX_ARRAY[v >>> 4];
hexChars[j * 2 + 1] = HEX_ARRAY[v & 0x0F];
}
return new String(hexChars);
}
public static byte[] hexStringToByteArray(String s) {
int len = s.length();
byte[] data = new byte[len / 2];
for (int i = 0; i < len; i += 2) {
data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
+ Character.digit(s.charAt(i+1), 16));
}
return data;
}