【问题标题】:Hex string to ByteBuffer conversion with Java 8 streams使用 Java 8 流将十六进制字符串转换为 ByteBuffer
【发布时间】:2018-05-04 09:12:24
【问题描述】:

我正在寻找一种从文件中逐行读取十六进制字符串并将它们作为转换后的字节附加到某个 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() 返回的流的解决方案。

一般我喜欢this 的回答。谁能帮我把它翻译成基于 java-8 流的解决方案或从上面完成我的示例?

谢谢!

【问题讨论】:

  • 链接的答案与十六进制字符串解析有什么关系?

标签: java java-8 java-stream string-conversion


【解决方案1】:

您可以使用实用方法将行作为十六进制字符串解析为字节数组:

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())
    )
);

【讨论】:

    【解决方案2】:

    这看起来有点像xy problem,因为读取文件“逐行”已经是您尝试的解决方案的一部分,而您的实际任务不包括读取文件的任何要求“一行一行”。

    实际上,您希望处理源的所有十六进制数字,而不考虑行终止符,这是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.flip() 返回的Buffer 需要类型转换回ByteBuffer,而Scanner.findAll 不可用,但必须用反向端口替换就像this answer中的那个。

    【讨论】:

    • findWithinHorizon 带零有什么用?
    • @Eugene as the documentation 声明:“如果地平线是0,那么地平线将被忽略,此方法继续在输入中搜索,寻找指定的模式而不受限制 i>”。
    • 对,那为什么不只找到呢?好像我错过了什么
    • @Eugene 在Scanner 中没有“仅查找”。有next(pattern),但它需要用分隔符模式的匹配来分隔标记,所以它在语义上并不相同。
    猜你喜欢
    • 2020-10-26
    • 1970-01-01
    • 2018-01-31
    • 2018-01-22
    • 1970-01-01
    • 2015-06-21
    • 2014-10-24
    • 2013-02-07
    • 2014-03-10
    相关资源
    最近更新 更多