Writing part:
for (int i = 0; i < prices.length; i++) {
buf.putDouble(prices[i]);
buf.putInt(units[i]);
byte[] descsBytes = descs[i].getBytes();
buf.putInt(descsBytes.length);
buf.put(descsBytes);
}
Reading part:
while (buf.hasRemaining()) {
price = buf.getDouble();
unit = buf.getInt();
int len = buf.getInt();
byte[] bytes = new byte[len];
buf.get(bytes);
desc = new String(bytes);
System.out.format("You ordered %d" + " units of %s at $%.2f%n",
unit, desc, price);
total += unit * price;
}
Answer from user3161880 on Stack OverflowWriting part:
for (int i = 0; i < prices.length; i++) {
buf.putDouble(prices[i]);
buf.putInt(units[i]);
byte[] descsBytes = descs[i].getBytes();
buf.putInt(descsBytes.length);
buf.put(descsBytes);
}
Reading part:
while (buf.hasRemaining()) {
price = buf.getDouble();
unit = buf.getInt();
int len = buf.getInt();
byte[] bytes = new byte[len];
buf.get(bytes);
desc = new String(bytes);
System.out.format("You ordered %d" + " units of %s at $%.2f%n",
unit, desc, price);
total += unit * price;
}
ByteBuffer.asCharBuffer() has a somewhat own behaviour. It creates a new buffer that shares the content with the ByteBuffer but has an own, independent position & limit.
So put() and get() on the CharBuffer do not change the position on the ByteBuffer!
So I would refrain from using ByteBuffer.asCharBuffer() at all as long as this is not exactly what you need.
So the solution would be to write and read the bytes into the original ByteBuffer:
buf.putInt(descs[i].length);
buf.put(descs[i].getBytes());`
To read the string again read the length and allocate the byte[] yourself:
int stringLength = buf.getInt();
byte[] bytes = new byte[stringLength];
buf.get(bytes);
desc = new String(bytes);
There is simpler approach to decode a ByteBuffer into a String without any problems, mentioned by Andy Thomas.
String s = StandardCharsets.UTF_8.decode(byteBuffer).toString();
EDIT (2018): The edited sibling answer by @xinyongCheng is a simpler approach, and should be the accepted answer.
Your approach would be reasonable if you knew the bytes are in the platform's default charset. In your example, this is true because k.getBytes() returns the bytes in the platform's default charset.
More frequently, you'll want to specify the encoding. However, there's a simpler way to do that than the question you linked. The String API provides methods that converts between a String and a byte[] array in a particular encoding. These methods suggest using CharsetEncoder/CharsetDecoder "when more control over the decoding [encoding] process is required."
To get the bytes from a String in a particular encoding, you can use a sibling getBytes() method:
byte[] bytes = k.getBytes( StandardCharsets.UTF_8 );
To put bytes with a particular encoding into a String, you can use a different String constructor:
String v = new String( bytes, StandardCharsets.UTF_8 );
Note that ByteBuffer.array() is an optional operation. If you've constructed your ByteBuffer with an array, you can use that array directly. Otherwise, if you want to be safe, use ByteBuffer.get(byte[] dst, int offset, int length) to get bytes from the buffer into a byte array.
java - Inserting a string into a bytebuffer - Stack Overflow
java - Why can't I add a String to a ByteBuffer? - Stack Overflow
java - Put a StringBuffer in a ByteBuffer - Stack Overflow
ByteBuffer to String
- Allocate a new
ByteBufferand set its size to a number large enough in order to avoid buffer to overflow when putting bytes to it - Use the
asCharBuffer()API method so as to be able to put characters directly into the byte buffer Using the
put(String)API method we can put a String directly to the byte bufferThe
toString()API method returns the string representation of theByteBuffer’s contents. Do not forget toflip()theByteBuffersince thetoString()API method displaysByteBuffer’s contents from the current buffer’s position on-wards:
UseByteBufferToStoreStrings:
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
public class UseByteBufferToStoreStrings {
public static void main(String[] args) {
// Allocate a new non-direct byte buffer with a 50 byte capacity
// set this to a big value to avoid BufferOverflowException
ByteBuffer buf = ByteBuffer.allocate(50);
// Creates a view of this byte buffer as a char buffer
CharBuffer cbuf = buf.asCharBuffer();
// Write a string to char buffer
cbuf.put("Your sting");
// Flips this buffer. The limit is set to the current position and then
// the position is set to zero. If the mark is defined then it is discarded
cbuf.flip();
String s = cbuf.toString(); // a string
System.out.println(s);
}
}
read more...
If you would like to add a String to a ByteBuffer using the return from it's getBytes() method works.
buf.put("Your string".getBytes);
Hi Guys,
Could anyone please tell me how I can convert a ByteBuffer to a String? I thought it would be a simple function but I can't seem to work it out.
This is just a part of what I have written. I am essentially trying to read a message sent via TCP. There were intermediate steps which I didn't think were necessary to include here.
ServerSocketChannel tcpserver = ServerSocketChannel.open(); SocketChannel connectionSocket = tcpserver.accept(); // create buffer to hold message ByteBuffer buf = ByteBuffer.allocate(48); int bytesRead = connectionSocket.read(buf);
While googling around, there was talk of paying attention to character encoding. Could someone please explain how this factors into the problem at hand?
Any help would be greatly appreciated!!
Kind Regards,
Giri
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.
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.
Check out the CharsetEncoder and CharsetDecoder API descriptions - You should follow a specific sequence of method calls to avoid this problem. For example, for CharsetEncoder:
- Reset the encoder via the
resetmethod, unless it has not been used before; - Invoke the
encodemethod zero or more times, as long as additional input may be available, passingfalsefor the endOfInput argument and filling the input buffer and flushing the output buffer between invocations; - Invoke the
encodemethod one final time, passingtruefor the endOfInput argument; and then - Invoke the
flushmethod so that the encoder can flush any internal state to the output buffer.
By the way, this is the same approach I am using for NIO although some of my colleagues are converting each char directly to a byte in the knowledge they are only using ASCII, which I can imagine is probably faster.
Unless things have changed, you're better off with
public static ByteBuffer str_to_bb(String msg, Charset charset){
return ByteBuffer.wrap(msg.getBytes(charset));
}
public static String bb_to_str(ByteBuffer buffer, Charset charset){
byte[] bytes;
if(buffer.hasArray()) {
bytes = buffer.array();
} else {
bytes = new byte[buffer.remaining()];
buffer.get(bytes);
}
return new String(bytes, charset);
}
Usually buffer.hasArray() will be either always true or always false depending on your use case. In practice, unless you really want it to work under any circumstances, it's safe to optimize away the branch you don't need.