在Java中将ByteBuffer转换为字符串

8

我有一个从ByteBuffer获取的byte[] bytes,其中包含数据包。我想将该数据包放入一个String中。

目前我的代码如下:

    byte[] bytes = packet.array();
    System.out.println("Packet String:" + new String(bytes));

但是最后我得到的输出如下:
Packet String:E����<Ҧ@��@�.
03-22 04:30:28.187   9296-10152/willem.com.vpn I/System.out﹕ ����J}�j���k�:����������9�������
03-22 04:30:28.197   9296-10152/willem.com.vpn I/System.out﹕ ��&�4��������ddrarpa��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������

我已经尝试了这种编码方式

System.out.println("Packet String:" + new String(bytes, Charset.forName("UTF-8")));

但那不是正确的字符集。有人能告诉我正确的字符集是什么吗?

没有人能告诉你自己文件的正确字符集是什么。只有你或者写入它的软件才能确定。 - user207421
以下JAVA NIO ByteBuffer基本使用参考解释了为什么你读到了垃圾信息。 - Pau Coma Ramirez
4个回答

12

你需要使用缓冲区的位置和限制来确定需要读取的字节数。

// ...populate the buffer...
buffer.flip(); // flip the buffer for reading
byte[] bytes = new byte[buffer.remaining()]; // create a byte array the length of the number of bytes written to the buffer
buffer.get(bytes); // read the bytes that were written
String packet = new String(bytes);

在我看来,你根本不应该使用array()。这是一种不好的做法。直接字节缓冲区(由ByteBuffer.allocateDirect()创建)没有后备数组,并且在尝试调用ByteBuffer.array()时会抛出异常。因此,为了可移植性,您应该尝试使用标准缓冲区getput方法。当然,如果你真的想使用数组,可以使用ByteBuffer.hasArray()检查缓冲区是否有后备数组。


1
然后我得到一个 java.nio.BufferUnderflowException,而字符串仍然没有意义。 - Liam de Haas
听起来你可能需要先调用 flip。当缓冲区被写入后,在再次读取之前,通常需要调用 flip 来重置位置和限制。flip 将把位置设置为 0,将限制设置为当前的 position,从而使 remaining 等于写入缓冲区的字节数。 - kuujo
我需要在哪里调用它? - Liam de Haas
通常在缓冲区写入任何内容后,您应该调用flipflip 用于在写入和从缓冲区读取之间切换。当写入字节时,缓冲区的位置会向前移动。为了从中读取,您需要将缓冲区翻转以从开头开始读取。ByteBuffer API 有点令人困惑,并且可能需要一段时间才能理解。 - kuujo
BufferUnderflowException 是在从缓冲区读取时发生的异常,它表示缓冲区的 limit 减去其 position 小于您尝试读取的字节数。例如,如果我正在尝试读取一个 long(8个字节),并且缓冲区的 position10,而它的 limitcapacity12,那么它将抛出一个 BufferUnderflowException 异常。写入时则相反,会抛出 BufferOverflowException - kuujo
1
我认为你需要在从缓冲区中读取任何内容之前立即调用 flip(),并在紧接着的操作中立即调用compact()。最好将缓冲区始终保持为可写入(和读取)状态,并且仅在尽可能短的时间内处于翻转状态。 - user207421

1

关于设置范围从位置到限制的答案在一般情况下是不正确的。当缓冲区已经部分消耗或者引用数组的一部分时(可以使用ByteBuffer.wrap从给定偏移量而不一定从开头开始),我们必须在计算中考虑到这一点。以下是适用于所有情况的通用解决方案(不包括编码):

if (myByteBuffer.hasArray()) {
    return new String(myByteBuffer.array(),
        myByteBuffer.arrayOffset() + myByteBuffer.position(),
        myByteBuffer.remaining());
} else {
    final byte[] b = new byte[myByteBuffer.remaining()];
    myByteBuffer.duplicate().get(b);
    return new String(b);
}

有关编码方面的问题,请参阅安迪·托马斯的回答。


这是对我有效的解决方案。谢谢。 - David Pursehouse

0
你忽略了缓冲区的限制。你应该先调用flip()方法来翻转缓冲区,然后再进行操作。
new String(buffer.array(), 0, buffer.position())

然后重置它。

但是你应该使用 Charset 解码器来生成一个 CharBuffer。至少你应该为 String 构造函数指定一个字符集。


你可以创建一个CharsetDecoder对象,然后调用CharsetDecoder.decode(ByteBuffer)方法将ByteBuffer解码为CharBuffer,最后直接将其转换为String - user207421
但我不知道要使用哪种字符集,我该如何找出来? - Liam de Haas
没有一个很好的方法可以知道任意字节数组的字符集。你需要提前知道那个东西。有一些库,比如Apache Tika,提供了字符集的猜测,但是像任何格式猜测一样,这是一门不完美的科学。 - kuujo
应该指定编码,但更重要的是,buffer.limit() 是一个偏移量,而不是长度。构造函数需要使用字节的长度。 - Yuri Schimke
你应该调用flip()函数来翻转buffer,然后调用“this”函数将position重置为零。因此,“new String(buffer.array(), 0, buffer.position())”不能正常工作。 - cpx

-1

ByteBuffer data = ...

new String(data.array(), "UTF-8");


array() 将返回 ByteBuffer 的后备数组。 - Franz Wong

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接