【问题标题】:Read NUL-terminated String from ByteBuffer从 ByteBuffer 中读取以 NUL 结尾的字符串
【发布时间】:2020-12-14 01:26:18
【问题描述】:

如何从 Java ByteBufferByteBuffer#position() 开始读取以 NUL 结尾的 UTF-8 字符串?

ByteBuffer b = /* 61 62 63 64 00 31 32 34 00 (hex) */;
String s0 = /* read first string */;
String s1 = /* read second string */;

// `s0` will now contain “ABCD” and `s1` will contain “124”.

我已经尝试过使用Charsets.UTF_8.decode(b),但这个函数似乎忽略了当前的ByteBuffer 位置并一直读取到缓冲区结束。

有没有比寻找包含 0 的字节并将缓冲区限制为它(或将带有字符串的部分复制到单独的缓冲区)更惯用的方法从字节缓冲区读取此类字符串?

【问题讨论】:

  • 这不是代码编写服务。发表您自己的努力并告诉我们您遇到了什么问题。
  • 对于较低级别的功能,我会查看CharsetDecoder...
  • 呃,读取字符直到你得到 NUL?不清楚问题出在哪里。
  • 您是否尝试过在循环中逐字节读取并填充byte[],然后实例化new String( bytes[], StandardCharsets.UTF_8 );
  • @Jim,是的,但我认为它不必要地复杂,因为可以(至少在理论上)使用原始缓冲区。

标签: java nio bytebuffer text-decoding


【解决方案1】:

我所不知道的惯用语意思是“一个班轮”(不足为奇,因为 NUL 终止的字符串不是 Java 规范的一部分)。

我想出的第一件事是使用b.slice().limit(x) 仅在所需字节上创建轻量级视图(比将它们复制到任何地方更好,因为您可能可以直接使用缓冲区)

ByteBuffer b = ByteBuffer.wrap(new byte[] {0x61, 0x62, 0x63, 0x64, 0x00, 0x31, 0x32, 0x34, 0x00 });
int i;
while (b.hasRemaining()) {
  ByteBuffer nextString = b.slice(); // View on b with same start position
  for (i = 0; b.hasRemaining() && b.get() != 0x00; i++) {
    // Count to next NUL
  }
  nextString.limit(i); // view now stops before NUL
  CharBuffer s = StandardCharsets.UTF_8.decode(nextString);
  System.out.println(s);
}

【讨论】:

    【解决方案2】:

    在 java 中,字符 \u0000,UTF-8 字节 0,Unicode 代码点 U+0 是一个普通字符。所以读完所有(可能是一个过大的字节数组),然后做

    String s = new String(bytes, StandardCharsets.UTF_8);
    
    String[] s0s1 = s.split("\u0000");
    String s0 = s0s1[0];
    String s1 = s0s1[1];
    

    如果您没有固定位置并且必须顺序读取每个字节,则代码很难看。其中一位 C 创始人确实将 nul 终止字符串称为历史性错误。

    相反,为了不为 java 字符串生成 UTF-8 字节 0,通常用于进一步处理为 C/C++ nul 终止的字符串,存在编写修改后的 UTF-8,也对 0 字节进行编码。

    【讨论】:

      【解决方案3】:

      您可以通过 replacesplit 函数来实现。将您的十六进制字节转换为字符串并通过自定义字符查找 0。然后用那个自定义字符分割你的字符串。

      import java.nio.ByteBuffer;
      import java.nio.charset.StandardCharsets;
      import java.util.Arrays;
      
      /**
       * Created by Administrator on 8/25/2020.
       */
      public class Jtest {
          public static void main(String[] args) {
              //ByteBuffer b = /* 61 62 63 64 00 31 32 34 00 (hex) */;
              ByteBuffer b = ByteBuffer.allocate(10);
      
              b.put((byte)0x61);
              b.put((byte)0x62);
              b.put((byte)0x63);
              b.put((byte)0x64);
              b.put((byte)0x00);
              b.put((byte)0x31);
              b.put((byte)0x32);
              b.put((byte)0x34);
              b.put((byte)0x00);
              b.rewind();
      
              String s0;
              String s1;
      
              // print the ByteBuffer
              System.out.println("Original ByteBuffer:  "
                      + Arrays.toString(b.array()));
      
              // `s0` will now contain “ABCD” and `s1` will contain “124”.
              String s = StandardCharsets.UTF_8.decode(b).toString();
              String ss = s.replace((char)0,';');
              String[] words = ss.split(";");
              for(int i=0; i < words.length; i++) {
                  System.out.println(" Word " + i + " = " +words[i]);
              }
      
          }
      }
      

      我相信你可以通过删除替换更有效地做到这一点。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-07-20
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多