Я ищу способ преобразовать длинную строку (из дампа), представляющую шестнадцатеричные значения в массив байтов.
Я не мог бы сформулировать это лучше, чем автор сообщения тот же вопрос здесь.
Но чтобы сохранить оригинальность, я сформулирую это по-своему: предположим, у меня есть строка "00A0BF", которую я хотел бы интерпретировать как
byte[] {0x00,0xA0,0xBf}
Что я должен делать?
Я новичок в Java, и в итоге я использовал BigInteger и следил за ведущими шестнадцатеричными нулями. Но я думаю, что это некрасиво, и я уверен, что упускаю что-то простое.
Приручил BigIntegerздесь.
FWIW String.getBytes() не будет работать так, как вы думаете. Пришлось усвоить это на собственном горьком опыте. if ("FF".getBytes() != "ff".getBytes()) { System.out.println("Try again"); }




Класс Hex в commons-codec должен сделать это за вас.
http://commons.apache.org/codec/
import org.apache.commons.codec.binary.Hex;
...
byte[] decoded = Hex.decodeHex("00A0BF");
// 0x00 0xA0 0xBF
Это тоже хорошо выглядит. См. Org.apache.commons.codec.binary.Hex.decodeHex ()
Это было интересно. Но мне было трудно следовать их решению. Есть ли у него какие-либо преимущества по сравнению с тем, что вы предложили (кроме проверки четного количества символов)?
Я всегда использовал такой метод, как
public static final byte[] fromHexString(final String s) {
String[] v = s.split(" ");
byte[] arr = new byte[v.length];
int i = 0;
for(String val: v) {
arr[i++] = Integer.decode("0x" + val).byteValue();
}
return arr;
}
этот метод разбивается на шестнадцатеричные значения, разделенные пробелами, но нетрудно заставить его разбить строку по любым другим критериям, например, на группы из двух символов.
Конкатенация строк не требуется. Просто используйте Integer.valueOf (val, 16).
Я пробовал использовать такие преобразования системы счисления раньше, и у меня были смешанные результаты
спасибо - как ни странно, он отлично работает с этой строкой: «9C001C» или «001C21» и не работает с этой: «9C001C21» Исключение в потоке «main» java.lang.NumberFormatException: для входной строки: «9C001C21» на java.lang. NumberFormatException.forInputString (неизвестный источник)
(Это не более странно, чем в случае Byte / byte: старший бит установлен без начала -)
Думаю сделаю это за тебя. Я собрал его из аналогичной функции, которая возвращала данные в виде строки:
private static byte[] decode(String encoded) {
byte result[] = new byte[encoded/2];
char enc[] = encoded.toUpperCase().toCharArray();
StringBuffer curr;
for (int i = 0; i < enc.length; i += 2) {
curr = new StringBuffer("");
curr.append(String.valueOf(enc[i]));
curr.append(String.valueOf(enc[i + 1]));
result[i] = (byte) Integer.parseInt(curr.toString(), 16);
}
return result;
}
Во-первых, вам не нужно преобразовывать строку в верхний регистр. Во-вторых, можно добавлять символы непосредственно в StringBuffer, что должно быть намного эффективнее.
Обновлено: как указано @mmyers, этот метод не работает с вводом, который содержит подстроки, соответствующие байтам с установленным старшим битом («80» - «FF»). Объяснение находится в Идентификатор ошибки: 6259307 Byte.parseByte не работает, как указано в документации SDK.
public static final byte[] fromHexString(final String s) {
byte[] arr = new byte[s.length()/2];
for ( int start = 0; start < s.length(); start += 2 )
{
String thisByte = s.substring(start, start+2);
arr[start/2] = Byte.parseByte(thisByte, 16);
}
return arr;
}
Закрыть, но этот метод не работает на данном входе "00A0BBF". См. bugs.sun.com/bugdatabase/view_bug.do?bug_id=6259307.
Также как ни странно это не касается «9С».
@mmyers: эй. Это не хорошо. Извините за путаницу. @ravigad: 9C имеет ту же проблему, потому что в этом случае установлен старший бит.
(byte) Short.parseShort (thisByte, 16) решает эту проблему
Вот метод, который действительно работает (на основе нескольких предыдущих полуправильных ответов):
private static byte[] fromHexString(final String encoded) {
if ((encoded.length() % 2) != 0)
throw new IllegalArgumentException("Input string must contain an even number of characters");
final byte result[] = new byte[encoded.length()/2];
final char enc[] = encoded.toCharArray();
for (int i = 0; i < enc.length; i += 2) {
StringBuilder curr = new StringBuilder(2);
curr.append(enc[i]).append(enc[i + 1]);
result[i/2] = (byte) Integer.parseInt(curr.toString(), 16);
}
return result;
}
Единственная возможная проблема, которую я вижу, - слишком длинная входная строка; вызов toCharArray () делает копию внутреннего массива строки.
Обновлено: Да, кстати, байты подписаны в Java, поэтому ваша входная строка преобразуется в [0, -96, -65] вместо [0, 160, 191]. Но вы, наверное, это уже знали.
Спасибо, Майкл - ты спасатель! Работа над проектом BlackBerry и попытка преобразовать строковое представление байта обратно в байт ... с помощью метода RIM «Byte.parseByte (byteString, 16)». Продолжал бросать NumberFormatExcpetion. Потратил часы, пытаясь понять, почему. Ваше предложение «Integer.praseInt ()» помогло. Еще раз спасибо!!
На самом деле, я думаю, что BigInteger - это очень хорошее решение:
new BigInteger("00A0BF", 16).toByteArray();
Обновлено: Небезопасно для ведущих нулей, как отмечено на плакате.
Я тоже так думал изначально. И спасибо за документирование - я просто подумал, что должен ... он делал некоторые странные вещи, хотя я действительно не понимал - например, опускал некоторые ведущие 0x00, а также смешивал порядок 1 байта в строке из 156 байтов. играл с.
Это хороший момент, когда нужно начинать нули. Я не уверен, что верю, что он может перепутать порядок байтов, и мне было бы очень интересно увидеть, как это продемонстрировано.
да, как только я это сказал, я тоже не поверил :) Я провел сравнение байтового массива из BigInteger с mmyers'fromHexString и (без 0x00) с ошибочной строкой - они были идентичны. «Путаница» действительно произошла, но могло быть что-то еще. Я посмотрю повнимательнее завтра
Проблема с BigInteger в том, что должен быть «знаковый бит». Если в ведущем байте установлен старший бит, то результирующий массив байтов имеет дополнительный 0 в 1-й позиции. Но все же +1.
Вот решение, которое, на мой взгляд, лучше, чем все опубликованные до сих пор:
/* s must be an even-length string. */
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;
}
Причины улучшения:
Безопасно с ведущими нулями (в отличие от BigInteger) и с отрицательными байтовыми значениями (в отличие от Byte.parseByte)
Не преобразует String в char[] и не создает объекты StringBuilder и String для каждого байта.
Нет зависимостей библиотеки, которые могут быть недоступны
Не стесняйтесь добавлять проверку аргументов через assert или исключения, если известно, что аргумент небезопасен.
Спасибо. Для этого должен быть встроенный. Тем более, что Byte.parseByte хрипит на отрицательных значениях громоздко.
Дает неверный результат. См. Реализацию apache в сообщении ниже.
Можете ли вы привести пример, который неправильно декодирован, или объясните, в чем его ошибка?
Это не работает для строки «0». Выдает исключение java.lang.StringIndexOutOfBoundsException
«0» - недопустимый ввод. Для каждого байта требуется две шестнадцатеричные цифры. Как отмечается в ответе: «Не стесняйтесь добавлять проверку аргумента ... если известно, что аргумент небезопасен».
javax.xml.bind.DatatypeConverter.parseHexBinary (hexString) кажется примерно на 20% быстрее, чем вышеупомянутое решение в моих микротестах (независимо от того, сколько они стоят), а также правильно выбрасывает исключения при недопустимом вводе (например, "gg" не является допустимым hexString, но вернет -77, используя предложенное решение).
Я не понимаю этого: безопасно с ведущими нулями (в отличие от BigInteger) и с отрицательными байтовыми значениями (в отличие от Byte.parseByte), насколько эти два небезопасны? Не могли бы вы привести примеры для тестирования?
@MuhammadAnnaqeeb См. Другие ответы ниже, в которых используется BigInteger или Byte.parseByte.
@Dave Разве здесь не должен использоваться логический сдвиг вместо арифметического?
@ChefPharaoh В Java есть только один оператор сдвига влево, который имеет тот же эффект, что и логический или арифметический сдвиг. См. en.wikipedia.org/wiki/Logical_shift
Как выглядит шестнадцатеричный дамп. Кто-нибудь может привести пример
Мне интересно, может ли data[i / 2] = (byte) (((Character.digit(s.charAt(i), 16) << 4) | Character.digit(s.charAt(i + 1), 16))); быть чуть более оптимальным? Или может потребоваться маска 0xFF.
Если оператор for изменен на for (int i = 0; i < len - 1; i += 2), функция больше не будет генерировать исключения для данных недопустимой длины. В этих случаях он все равно не вернет правильные данные, но, по крайней мере, сбой больше не будет.
@DaedalusAlpha Это зависит от вашего контекста, но обычно я считаю, что с такими вещами лучше быстро и громко терпеть неудачу, чтобы вы могли исправить свои предположения, а не молча возвращать неверные данные.
Работает как шарм. Для индекса, выходящего за границы, введите шестнадцатеричную строку префикса с «0», чтобы сделать строку любого размера, который вам нужен для вывода - например, если вы конвертируете двухбайтовую шестнадцатеричную строку, сделайте это перед вызовом метода StringUtils .leftPad (hextString, 4, «0»). Два символа в шестнадцатеричной строке преобразуются в 1 байт.
Я добавил причину ... при разработке для android / linux / windows / macosx / ios в одной исходной базе это решение всегда работает.
Я категорически не согласен с тем, что «0 - недопустимый ввод». 0 - это вполне допустимая шестнадцатеричная строка. Тот факт, что это небезопасно для этого кода, не означает, что он недействителен. Тот факт, что каждый байт обычно выражается двумя шестнадцатеричными символами, не означает, что 0 не является допустимой шестнадцатеричной строкой; шестнадцатеричные строки могут быть преобразованы во многие вещи помимо байтовых массивов. Если функция требует в качестве входных данных шестнадцатеричную строку четной длины, это следует четко задокументировать, вместо того, чтобы оставлять пользователю возможность анализировать код, чтобы узнать, каковы его предположения. garethrees.org/2014/05/08/heartbleed
@LarsH Спасибо за напоминание о том, что всегда хорошо четко выражать свои предположения. В контексте этого вопроса о шестнадцатеричном дампе с примером использования пар символов, включая ведущие нули, я считаю справедливым предположение, что данные должны представлять байты с использованием двух символов каждый. Однако я могу представить себе домены, в которых можно было бы разрешить вместо этого особый случай одиночного или ведущего символа «0». Я согласен с тем, что если эта функция является частью общедоступного API, это поведение в любом случае должно быть задокументировано, как в org.apache.commons.codec.binary.Hex и типах данных XML.
Справедливо. В заголовке вопроса написано «шестнадцатеричный дамп», а не просто «шестнадцатеричная строка». Кстати, я просто использовал ваш код в производственном проекте. (И добавил проверку для строк нечетной длины.) Так что спасибо.
P.S. Я внес предлагаемую правку, чтобы задокументировать это предположение. Очевидно, вы можете изменить его по своему желанию.
104207194088 TESTINGG 4304 GG2741 ��ôCONN��� | �� $ GK23000023R00P08TESTINGG1042071940882010260720 03BVodafone IN�������������; �� -> Получение "??" тип ответа при получении данных через сокет дейтаграммы. Любое предложение, где я ошибаюсь?
Метод BigInteger() из java.math очень медленный и не рекомендуется.
Integer.parseInt(HEXString, 16)
может вызвать проблемы с некоторыми персонажами без преобразование в цифру / целое число
Хороший рабочий метод:
Integer.decode("0xXX") .byteValue()
Функция:
public static byte[] HexStringToByteArray(String s) {
byte data[] = new byte[s.length()/2];
for(int i=0;i < s.length();i+=2) {
data[i/2] = (Integer.decode("0x"+s.charAt(i)+s.charAt(i+1))).byteValue();
}
return data;
}
Удачи, удачи
public static byte[] hex2ba(String sHex) throws Hex2baException {
if (1==sHex.length()%2) {
throw(new Hex2baException("Hex string need even number of chars"));
}
byte[] ba = new byte[sHex.length()/2];
for (int i=0;i<sHex.length()/2;i++) {
ba[i] = (Integer.decode(
"0x"+sHex.substring(i*2, (i+1)*2))).byteValue();
}
return ba;
}
Мне нравится решение Character.digit, но вот как я его решил
public byte[] hex2ByteArray( String hexString ) {
String hexVal = "0123456789ABCDEF";
byte[] out = new byte[hexString.length() / 2];
int n = hexString.length();
for( int i = 0; i < n; i += 2 ) {
//make a bit representation in an int of the hex value
int hn = hexVal.indexOf( hexString.charAt( i ) );
int ln = hexVal.indexOf( hexString.charAt( i + 1 ) );
//now just shift the high order nibble and add them together
out[i/2] = (byte)( ( hn << 4 ) | ln );
}
return out;
}
HexBinaryAdapter обеспечивает возможность упорядочивания и отмены маршалинга между String и byte[].
import javax.xml.bind.annotation.adapters.HexBinaryAdapter;
public byte[] hexToBytes(String hexString) {
HexBinaryAdapter adapter = new HexBinaryAdapter();
byte[] bytes = adapter.unmarshal(hexString);
return bytes;
}
Это просто пример, который я ввел ... Я просто использую его как есть, и мне не нужно создавать отдельный метод для его использования.
Он работает, только если входная строка (hexString) имеет четное количество символов. В противном случае: исключение в потоке «main» java.lang.IllegalArgumentException: hexBinary должен быть четной длины:
О, спасибо, что указали на это. У пользователя действительно не должно быть нечетного количества символов, потому что массив байтов представлен как {0x00,0xA0,0xBf}. Каждый байт состоит из двух шестнадцатеричных цифр или полубайта. Таким образом, любое количество байтов всегда должно содержать четное количество символов. Спасибо, что упомянули об этом.
Вы можете использовать java.xml.bind.DatatypeConverter.parseHexBinary (hexString) напрямую вместо использования HexBinaryAdapter (который, в свою очередь, вызывает DatatypeConverter). Таким образом, вам не нужно создавать объект экземпляра адаптера (поскольку методы DatatypeConverter статичны).
javax.xml.bind. * больше не доступен в Java 9. Опасно то, что код, использующий его, будет компилироваться под Java 1.8 или более ранней версии (Java 9 с исходными настройками до более ранней версии), но получит исключение времени выполнения, работающее под Java 9.
Однострочные:
import javax.xml.bind.DatatypeConverter;
public static String toHexString(byte[] array) {
return DatatypeConverter.printHexBinary(array);
}
public static byte[] toByteArray(String s) {
return DatatypeConverter.parseHexBinary(s);
}
Предупреждения:
eckes)Fabian за это), но вы можете просто взять исходный код, если в вашей системе по какой-то причине отсутствует javax.xml. Спасибо @ Bert Regelink за извлечение исходного кода.IMHO, это должен быть принятый / лучший ответ, поскольку он короткий и чистить (в отличие от ответа @DaveL) и не требует каких-либо внешних библиотек (например, ответ skaffman). Также <Введите старый анекдот об изобретении велосипеда>.
например, класс datatypeconverter недоступен в Android.
Предупреждение: в Java 9 Jigsaw это больше не является частью (по умолчанию) корневого набора java.se, поэтому результатом будет ClassNotFoundException, если вы не укажете --add-modules java.se.ee.
Удивительный ответ, мог декодировать строковый запрос base64, использовать шестнадцатеричный для преобразования массива байтов с предоставленным решением, и он работал как шарм
@dantebarba Я думаю, что javax.xml.bind.DatatypeConverter уже предоставляет метод кодирования / декодирования данных Base64. См. parseBase64Binary() и printBase64Binary().
может подтвердить, что в зависимости от javax.xml.bind может вызвать java.lang.NoClassDefFoundError.
Чтобы добавить к проблемам с DataTypeConverter, Java SE 11 полностью удалил JAXB API и теперь включен только в Java EE. Вы также можете добавить его как зависимость Maven, как предлагается здесь: stackoverflow.com/a/43574427/7347751
Я нашел Kernel Panic как наиболее полезное для меня решение, но столкнулся с проблемами, если шестнадцатеричная строка была нечетным числом. решил это так:
boolean isOdd(int value)
{
return (value & 0x01) !=0;
}
private int hexToByte(byte[] out, int value)
{
String hexVal = "0123456789ABCDEF";
String hexValL = "0123456789abcdef";
String st = Integer.toHexString(value);
int len = st.length();
if (isOdd(len))
{
len+=1; // need length to be an even number.
st = ("0" + st); // make it an even number of chars
}
out[0]=(byte)(len/2);
for (int i =0;i<len;i+=2)
{
int hh = hexVal.indexOf(st.charAt(i));
if (hh == -1) hh = hexValL.indexOf(st.charAt(i));
int lh = hexVal.indexOf(st.charAt(i+1));
if (lh == -1) lh = hexValL.indexOf(st.charAt(i+1));
out[(i/2)+1] = (byte)((hh << 4)|lh);
}
return (len/2)+1;
}
Я добавляю несколько шестнадцатеричных чисел в массив, поэтому передаю ссылку на массив, который я использую, и int, который мне нужно преобразовать, и возвращаю относительное положение следующего шестнадцатеричного числа. Итак, последний байтовый массив содержит [0] количество шестнадцатеричных пар, [1 ...] шестнадцатеричных пар, затем количество пар ...
One-liners:
import javax.xml.bind.DatatypeConverter; public static String toHexString(byte[] array) { return DatatypeConverter.printHexBinary(array); } public static byte[] toByteArray(String s) { return DatatypeConverter.parseHexBinary(s); }
Для тех из вас, кто интересуется фактическим кодом Однострочные из FractalizeR (мне это нужно, поскольку javax.xml.bind недоступен для Android (по умолчанию)), это исходит из com.sun.xml.internal.bind.DatatypeConverterImpl.java:
public byte[] parseHexBinary(String s) {
final int len = s.length();
// "111" is not a valid hex encoding.
if ( len%2 != 0 )
throw new IllegalArgumentException("hexBinary needs to be even-length: "+s);
byte[] out = new byte[len/2];
for( int i=0; i<len; i+=2 ) {
int h = hexToBin(s.charAt(i ));
int l = hexToBin(s.charAt(i+1));
if ( h==-1 || l==-1 )
throw new IllegalArgumentException("contains illegal character for hexBinary: "+s);
out[i/2] = (byte)(h*16+l);
}
return out;
}
private static int hexToBin( 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 char[] hexCode = "0123456789ABCDEF".toCharArray();
public String printHexBinary(byte[] data) {
StringBuilder r = new StringBuilder(data.length*2);
for ( byte b : data) {
r.append(hexCode[(b >> 4) & 0xF]);
r.append(hexCode[(b & 0xF)]);
}
return r.toString();
}
DatatypeConverter также по умолчанию недоступен в Java 9. Опасно то, что код, использующий его, будет компилироваться под Java 1.8 или более ранней версии (Java 9 с исходными настройками до более ранней версии), но получит исключение времени выполнения под Java 9 без "--add-modules java.se.ee".
Кодекс, представленный Бертом Регелинком, просто не работает. Попробуйте следующее:
import javax.xml.bind.DatatypeConverter;
import java.io.*;
public class Test
{
@Test
public void testObjectStreams( ) throws IOException, ClassNotFoundException
{
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
String stringTest = "TEST";
oos.writeObject( stringTest );
oos.close();
baos.close();
byte[] bytes = baos.toByteArray();
String hexString = DatatypeConverter.printHexBinary( bytes);
byte[] reconvertedBytes = DatatypeConverter.parseHexBinary(hexString);
assertArrayEquals( bytes, reconvertedBytes );
ByteArrayInputStream bais = new ByteArrayInputStream(reconvertedBytes);
ObjectInputStream ois = new ObjectInputStream(bais);
String readString = (String) ois.readObject();
assertEquals( stringTest, readString);
}
}
На самом деле это другая проблема и, вероятно, принадлежит к другому потоку.
Теперь для этого можно использовать BaseEncoding в guava.
BaseEncoding.base16().decode(string);
Чтобы изменить это, используйте
BaseEncoding.base16().encode(bytes);
Основываясь на предложенном решении, следующее должно быть немного более эффективным:
public static byte [] hexStringToByteArray (final String s) {
if (s == null || (s.length () % 2) == 1)
throw new IllegalArgumentException ();
final char [] chars = s.toCharArray ();
final int len = chars.length;
final byte [] data = new byte [len / 2];
for (int i = 0; i < len; i += 2) {
data[i / 2] = (byte) ((Character.digit (chars[i], 16) << 4) + Character.digit (chars[i + 1], 16));
}
return data;
}
Потому что: первоначальное преобразование в массив символов избавляет от проверки длины в charAt
Для меня это было решение, HEX = "FF01" затем разделить на FF (255) и 01 (01)
private static byte[] BytesEncode(String encoded) {
//System.out.println(encoded.length());
byte result[] = new byte[encoded.length() / 2];
char enc[] = encoded.toUpperCase().toCharArray();
String curr = "";
for (int i = 0; i < encoded.length(); i=i+2) {
curr = encoded.substring(i,i+2);
System.out.println(curr);
if (i==0){
result[i]=((byte) Integer.parseInt(curr, 16));
}else{
result[i/2]=((byte) Integer.parseInt(curr, 16));
}
}
return result;
}
На этот вопрос уже давно есть ответ, и у него есть несколько хороших альтернатив; К сожалению, на данный момент ваш ответ не дает существенного улучшения ценности.
В android, если вы работаете с hex, можете попробовать окио.
простое использование:
byte[] bytes = ByteString.decodeHex("c000060000").toByteArray();
и результат будет
[-64, 0, 6, 0, 0]
Я тестировал много разных методов, но этот как минимум в два раза быстрее!
Вот еще одна версия, которая поддерживает строки нечетной длины, не прибегая к конкатенации строк.
public static byte[] hexStringToByteArray(String input) {
int len = input.length();
if (len == 0) {
return new byte[] {};
}
byte[] data;
int startIdx;
if (len % 2 != 0) {
data = new byte[(len / 2) + 1];
data[0] = (byte) Character.digit(input.charAt(0), 16);
startIdx = 1;
} else {
data = new byte[len / 2];
startIdx = 0;
}
for (int i = startIdx; i < len; i += 2) {
data[(i + 1) / 2] = (byte) ((Character.digit(input.charAt(i), 16) << 4)
+ Character.digit(input.charAt(i+1), 16));
}
return data;
}
Мое формальное решение:
/**
* Decodes a hexadecimally encoded binary string.
* <p>
* Note that this function does <em>NOT</em> convert a hexadecimal number to a
* binary number.
*
* @param hex Hexadecimal representation of data.
* @return The byte[] representation of the given data.
* @throws NumberFormatException If the hexadecimal input string is of odd
* length or invalid hexadecimal string.
*/
public static byte[] hex2bin(String hex) throws NumberFormatException {
if (hex.length() % 2 > 0) {
throw new NumberFormatException("Hexadecimal input string must have an even length.");
}
byte[] r = new byte[hex.length() / 2];
for (int i = hex.length(); i > 0;) {
r[i / 2 - 1] = (byte) (digit(hex.charAt(--i)) | (digit(hex.charAt(--i)) << 4));
}
return r;
}
private static int digit(char ch) {
int r = Character.digit(ch, 16);
if (r < 0) {
throw new NumberFormatException("Invalid hexadecimal string: " + ch);
}
return r;
}
Похож на Функция PHP hex2bin (), но в стиле Java.
Пример:
String data = new String(hex2bin("6578616d706c65206865782064617461"));
// data value: "example hex data"
Если вы предпочитаете потоки Java 8 в качестве стиля кодирования, этого можно достичь, используя только примитивы JDK.
String hex = "0001027f80fdfeff";
byte[] converted = IntStream.range(0, hex.length() / 2)
.map(i -> Character.digit(hex.charAt(i * 2), 16) << 4 | Character.digit(hex.charAt((i * 2) + 1), 16))
.collect(ByteArrayOutputStream::new,
ByteArrayOutputStream::write,
(s1, s2) -> s1.write(s2.toByteArray(), 0, s2.size()))
.toByteArray();
Параметры , 0, s2.size() в функции конкатенации сборщика могут быть опущены, если вы не против перехвата IOException.
Поздно к вечеринке, но я объединил приведенный выше ответ DaveL в класс с обратным действием - на всякий случай, это помогает.
public final class HexString {
private static final char[] digits = "0123456789ABCDEF".toCharArray();
private HexString() {}
public static final String fromBytes(final byte[] bytes) {
final StringBuilder buf = new StringBuilder();
for (int i = 0; i < bytes.length; i++) {
buf.append(HexString.digits[(bytes[i] >> 4) & 0x0f]);
buf.append(HexString.digits[bytes[i] & 0x0f]);
}
return buf.toString();
}
public static final byte[] toByteArray(final String hexString) {
if ((hexString.length() % 2) != 0) {
throw new IllegalArgumentException("Input string must contain an even number of characters");
}
final int len = hexString.length();
final byte[] data = new byte[len / 2];
for (int i = 0; i < len; i += 2) {
data[i / 2] = (byte) ((Character.digit(hexString.charAt(i), 16) << 4)
+ Character.digit(hexString.charAt(i + 1), 16));
}
return data;
}
}
И тестовый класс JUnit:
public class TestHexString {
@Test
public void test() {
String[] tests = {"0FA1056D73", "", "00", "0123456789ABCDEF", "FFFFFFFF"};
for (int i = 0; i < tests.length; i++) {
String in = tests[i];
byte[] bytes = HexString.toByteArray(in);
String out = HexString.fromBytes(bytes);
System.out.println(in); //DEBUG
System.out.println(out); //DEBUG
Assert.assertEquals(in, out);
}
}
}
Я знаю, что это очень старая ветка, но все же хочу добавить свою копейку.
Если мне действительно нужно закодировать простой преобразователь шестнадцатеричной строки в двоичный, я бы хотел сделать это следующим образом.
public static byte[] hexToBinary(String s){
/*
* skipped any input validation code
*/
byte[] data = new byte[s.length()/2];
for( int i=0, j=0;
i<s.length() && j<data.length;
i+=2, j++)
{
data[j] = (byte)Integer.parseInt(s.substring(i, i+2), 16);
}
return data;
}
См. Также stackoverflow.com/questions/9655181/….