8

I have a byte[] bytes from a ByteBuffer which contains a packet. I want to put the packet in a String.

Currently I have the following

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

But then I end up with the following output

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��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������

I've tried it with encoding like this

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

But that isn't the right charset. Can anybody tell me what is?

Liam de Haas
  • 1,258
  • 3
  • 18
  • 39
  • *Nobody* can tell you what the correct Charset for your own file is. Only you, or the software that wrote it, can yield that. – user207421 Mar 22 '15 at 08:15
  • The following [reference on JAVA NIO ByteBuffer Basic Usage](http://tutorials.jenkov.com/java-nio/buffers.html) explains why you are reading garbaje. – Pau Coma Ramirez Sep 13 '16 at 12:44

4 Answers4

12

You need to use the buffer's position and limit to determine the number of bytes to read.

// ...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);

In my opinion you shouldn't really be using the backing array() at all; it's bad practice. Direct byte buffers (created by ByteBuffer.allocateDirect() won't have a backing array and will throw an exception when you try to call ByteBuffer.array(). Because of this, for portability you should try to stick to the standard buffer get and put methods. Of course, if you really want to use the array you can use ByteBuffer.hasArray() to check if the buffer has a backing array.

kuujo
  • 7,785
  • 1
  • 26
  • 21
  • 1
    Then i get a `java.nio.BufferUnderflowException` and the string still doesn't make sense – Liam de Haas Mar 22 '15 at 04:22
  • It sounds like you may need to call `flip` first. When the buffer is written, before you read it again you often need to call `flip` to reset the position and limit. `flip` will set the position to `0` and the limit to the current `position`, thus making `remaining` equivalent to the number of bytes that were written into the buffer. – kuujo Mar 22 '15 at 04:25
  • Where do I need to call it? – Liam de Haas Mar 22 '15 at 04:26
  • Normally you should call `flip` after any writing to the buffer. `flip` is meant to be used to switch between writing and reading from the buffer. When bytes are written, the buffer's position is advanced. In order to then read from it you need to `flip` the buffer to start back at the beginning for reading. The `ByteBuffer` API is a bit confusing and can take a while to understand. – kuujo Mar 22 '15 at 04:34
  • `BufferUnderflowException` happens when reading from the buffer and indicates that the buffer's `limit` minus its `position` is less than the number of bytes you're trying to read. So, for instance, if I'm trying to read a `long` (8 bytes) and the buffer's `position` is `10` and it's `limit` or `capacity` is `12` then it will throw a `BufferUnderflowException`. The opposite is true for writing and `BufferOverflowException` – kuujo Mar 22 '15 at 04:38
  • 1
    I would say you need to call `flip()` *just before* any *reading from* the buffer, and call `compact()` immediately afterwards. It's easiest to maintain buffers as always ready for writing to (and reading into), and only have them in the flipped state for as short a time as possible. – user207421 Mar 22 '15 at 04:50
1

The answer talking about setting range from position to limit is not correct in a general case. When the buffer has been partially consumed, or is referring to a part of an array (you can ByteBuffer.wrap an array at a given offset, not necessarily from the beginning), we have to account for that in our calculations. This is the general solution that works for buffers in all cases (does not cover encoding):

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

For the concerns related to encoding, see Andy Thomas' answer.

Alex Yarmula
  • 10,477
  • 5
  • 33
  • 32
0

You're ignoring the limit of the buffer. You should flip() the buffer, then call

new String(buffer.array(), 0, buffer.position())

Then reset it.

But you should really be using a Charset decoder to yield a CharBuffer for this. At least you should specify a character set to the String constructor.

user207421
  • 305,947
  • 44
  • 307
  • 483
  • You create it, call [`CharsetDecoder.decode(ByteBuffer)`](https://docs.oracle.com/javase/8/docs/api/java/nio/charset/CharsetDecoder.html#decode-java.nio.ByteBuffer-) to produce a `CharBuffer,` then turn that directly into a `String.` – user207421 Mar 22 '15 at 03:50
  • But I don't know what charset to use, how do I find out? – Liam de Haas Mar 22 '15 at 03:56
  • There's not a great way to know the character set of an arbitrary byte array. You sort of need to know that thing ahead of time. There are a few libraries like [Apache Tika](https://tika.apache.org/) which provide guesstimation of character sets, but as with any format guessing it's an imperfect science. – kuujo Mar 22 '15 at 04:17
  • should specify encoding, but more seriously buffer.limit() is an offset, not the length. the constructor wants teh length of bytes to use. – Yuri Schimke Jun 29 '16 at 15:50
  • `You should flip() the buffer, then call` this will reset the position to zero. Therefore, `new String(buffer.array(), 0, buffer.position())` won't work. – cpx Jun 18 '17 at 06:59
-1

ByteBuffer data = ...

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

chinthakad
  • 939
  • 2
  • 16
  • 31