Why doesn't my way of using FileChannel, ByteBuffer and CharBuffer work the same?
Given the file
Orange Purple Indigo Pink
Why is the method myWay
in the code below not giving me content ByteBuffer
through Charset.decode
? Note that I am checking what the ByteBuffer
content of the file has, but it seems that no matter what methodology I use from myWay
, I cannot get the generated CharBuffer
content. The method otherWay
works as expected. Does anyone know what's going on? I read the javdoc for ByteBuffer and CharBuffer but didn't really see anything that explains it (or I just missed it.) What's the difference if I used FileChannel.read
vs FileChannel.map
if I can show the contents of the buffer with read
?
public class Junk {
private static final int BUFFER_SIZE = 127;
private static final String CHARSET = "UTF-8";
public static void main(String[] args) {
try {
String fileName = "two.txt";
myWay(fileName);
otherWay(fileName);
} catch (IOException e) {
throw new IllegalStateException(e);
}
}
private static void myWay(String fileName) throws IOException {
System.out.println("I did it MY WAY!......");
FileChannel channel = FileChannel.open(Paths.get(fileName), StandardOpenOption.READ);
// I tried both `allocate` and `allocateDirect`
ByteBuffer buffer = ByteBuffer.allocateDirect(BUFFER_SIZE);
int bytesRead = channel.read(buffer);
channel.close();
// Manually build the string from the ByteBuffer.
// This is ONLY to validate the buffer has the content
StringBuilder sb = new StringBuilder();
for(int i=0;i<bytesRead;i++){
sb.append((char)buffer.get(i));
}
System.out.println("manual string='"+sb+"'");
CharBuffer charBuffer = Charset.forName(CHARSET).decode(buffer);
// WHY FOR YOU NO HAVE THE CHARS??!!
System.out.println("CharBuffer='" + new String(charBuffer.array()) + "'");
System.out.println("CharBuffer='" + charBuffer.toString() + "'");
System.out.println("........My way sucks.");
}
private static void otherWay(String fileName) throws IOException{
System.out.println("The other way...");
FileChannel channel = FileChannel.open(Paths.get(fileName), StandardOpenOption.READ);
ByteBuffer buffer = channel.map(FileChannel.MapMode.READ_ONLY, 0, channel.size());
channel.close();
Charset chars = Charset.forName(CHARSET);
CharBuffer cbuf = chars.decode(buffer);
String str = new String(cbuf.array());
System.out.println("str = '" + str + "'");
System.out.println("...works.");
}
}
Output:
I did it MY WAY! ...... manual string = 'Orange Purple Indigo Pink ' CharBuffer = '' CharBuffer = '' ........ My way sucks. The other way ... str = 'Orange Purple Indigo Pink ' ... works.
source to share
Simple and subtle: you don't rewind your buffer.
When you call FileChannel#read(ByteBuffer)
this method will advance the position()
buffers:
System.out.println("Before "+buffer.position()); // prints 0
int bytesRead = channel.read(buffer);
System.out.println("After "+buffer.position()); // prints 28
When you subsequently decode that into CharBuffer
, then you are essentially decoding exactly the 99 bytes that were never written (and they are all still 0
).
Just add
buffer.rewind(); // (or buffer.position(0))
buffer.limit(bytesRead);
after you've read data from the file pipe, so the method decode
grabs exactly the part that received the data.
source to share