Преобразование шестнадцатеричной строки в байтовый буфер с потоками 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 или завершить мой пример выше?

Благодарю вас!


person qdbp    schedule 04.05.2018    source источник
comment
Какое отношение связанный ответ имеет к разбору шестнадцатеричной строки?   -  person Holger    schedule 04.05.2018


Ответы (1)


Это немного похоже на проблему 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 недоступен, но должен быть заменен обратным портом, подобным тому, что в этот ответ.

person Holger    schedule 04.05.2018
comment
какая польза от findWithinHorizon с нулем? - person Eugene; 04.05.2018
comment
@Юджин как документация гласит: «Если горизонт равен 0, то горизонт игнорируется, и этот метод продолжает поиск по входным данным в поисках указанного шаблона без ограничений». - person Holger; 04.05.2018
comment
правильно, так почему бы не найти только? Кажется, я что-то упускаю - person Eugene; 04.05.2018
comment
@ Юджин, в Scanner нет «только найти». Есть next(pattern), но он требует, чтобы токены были разделены совпадениями шаблона-разделителя, поэтому он семантически не идентичен. - person Holger; 06.05.2018