Преобразование шестнадцатеричной строки в ByteBuffer с потоками Java 8
Я ищу способ читать шестнадцатеричные строки из файла строка за строкой и добавлять их как преобразованные байты в некоторый ByteBuffer.
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
Files.lines(filePath).foreach( l ->
byteBuffer.put(
// first of all strip newlines and normalize string
l.replaceAll("/\n|\r/g", "").toUpperCase()
// but what to do here?
// is there something like
// take next 2 characters (-> Consumer)
// and replace them with the converted byte?
// E.g. "C8" -> 0xC8
// until the end of the string is reached
)
);
На это ответили миллион раз. Но мне было интересно, если есть решение с использованием потоков, как возвращено Files.lines()
,
Вообще мне нравится этот ответ. Кто-нибудь может мне помочь с переводом этого в потоковое решение java-8 или завершением моего примера сверху?
Спасибо!
2 ответа
Вы можете использовать служебный метод для анализа строки как шестнадцатеричной строки в байтовом массиве:
public static byte[] hexStringToByteArray(String str) {
if(str.startsWith("0x")) { // Get rid of potential prefix
str = str.substring(2);
}
if(str.length() % 2 != 0) { // If string is not of even length
str = '0' + str; // Assume leading zeroes were left out
}
byte[] result = new byte[str.length() / 2];
for(int i = 0; i < str.length(); i += 2) {
String nextByte = str.charAt(i) + "" + str.charAt(i + 1);
// To avoid overflow, parse as int and truncate:
result[i / 2] = (byte) Integer.parseInt(nextByte, 16);
}
return result;
}
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
Files.lines(filePath).forEach( l ->
byteBuffer.put(
hexStringToByteArray(l.replaceAll("/\n|\r/g", "").toUpperCase())
)
);
Это немного похоже на проблему xy, так как чтение файла "строка за строкой" уже является частью вашего попытанного решения, в то время как ваша реальная задача не включает никаких требований для чтения файла "строка за строкой".
На самом деле, вы хотите обработать все шестнадцатеричные числа источника, независимо от ограничителей строки, что является задачей для java.util.Scanner
, Он также позволяет обрабатывать элементы с помощью Stream API, хотя эта конкретная задача не дает от этого большой пользы по сравнению с циклом:
ByteBuffer bb = ByteBuffer.allocate(1024);
try(Scanner s = new Scanner(yourFile)) {
s.findAll("[0-9A-Fa-f]{2}")
.mapToInt(m -> Integer.parseInt(m.group(), 16))
.forEachOrdered(i -> { if(bb.hasRemaining()) bb.put((byte)i); });
}
try(Scanner s = new Scanner(yourFile)) {
Pattern p = Pattern.compile("[0-9A-Fa-f]{2}");
for(;;) {
String next = s.findWithinHorizon(p, 0);
if(next == null) break;
if(!bb.hasRemaining()) // the thing hard to do with Stream API
bb = ByteBuffer.allocate(bb.capacity()*2).put(bb.flip());
bb.put((byte)Integer.parseInt(next, 16));
}
}
Обратите внимание, что в этих примерах используется Java 9. В Java 8 Buffer
вернулся Buffer.flip()
нуждается в приведении типа ByteBuffer
а также Scanner.findAll
недоступен, но должен быть заменен резервным портом, как в этом ответе.