Here we will learn how to write data in binary format. In the earlier post we learned how to write in text format
For that we create a FileOutputStream.Then we need to create a file buffer. Buffer capacity is maximum no of elements it can contain Buffer position is the next location from which it can read or write.
try(FileOutputStream binFile = new FileOutputStream("data.dat");
//After creating a FileOutputStream object above for the file we want to write we will create a channel
// below which we will use to write to the above file.
FileChannel binChannel = binFile.getChannel()){
//create a byte array we want to write to file
//and since we are using try with resources above it will automatically close the file output stream
//and channel we are creating above
byte[] outputBytes = "Hello world!".getBytes();
// create byte buffer.wrap to create byte buffer.
// wrap method wraps byte array to buffer.
// if we use buffer to write String or bytes and then use it to read from file ,
// the output byte array will be changed.
//so when we use wrap method we are telling run time to use the byte array we created above as the buffer
//wrap also sets the initial position of buffer to 0.
//wrap will set buffer capacity to byte array length
ByteBuffer buffer = ByteBuffer.wrap(outputBytes);
//we then use file channel to write our buffer
//Here we can simply have used binChannel.write(buffer).
//We are using numBytes simply to get the no of bytes we want to write.
int numBytes = binChannel.write(buffer);
//we can then print the no of bytes written
System.out.println("numBytes written was: "+numBytes);
//Now lets create a int buffer to write integers to file
ByteBuffer intBuffer = ByteBuffer.allocate(Integer.BYTES);
intBuffer.putInt(234);
//Now very imp thing to note here. When we created the int buffer above it will set position of buffer to 0.
//Then after we use putInt method it will set position of buffer to after the int.
//Then if we use write it will be positioned after the position of above int.
//So in order to read the data we have to reset the buffer position to 0 to read data
//after every write as we have done below.
//Please also note that for wrap method we never used flip as wrap takes care of resetting the
//buffer all by itself.
//Also another thing to note is for all other methods like putInt,putLong etc we don't have to use flip
//after every write. we can write 10 lines of code and use flip in the end.
//Also note we can use putInt to write to a specific index as well by passing the index as second
//argument to put method.
intBuffer.flip();
numBytes = binChannel.write(intBuffer);
System.out.println("numBytes written was: "+numBytes);
//Now if we try to write another integer to the file after using flip method above we got an error
//saying java.nio.BufferOverflowException
//The issue is after we write the last buffer the buffer position is set to 4. But 4 also the maximum position
//for our buffer as defined here as Integer is four bytes (ByteBuffer.allocate(Integer.BYTES)
// that is why the above error.
//so to solve this issue we have to flip before putInt method and before write as well.
intBuffer.flip();
intBuffer.putInt(-98765);
intBuffer.flip();
numBytes = binChannel.write(intBuffer);
System.out.println("numBytes written was: "+numBytes);
//So in a nutshell always call flip when flipping from reading and writing .
//also use it every time we are about to write data to file
Now let us read the data.dat file that we have written above using a RandomAccessFile
We define a byte array below and we define it to the length of String Hello World we defined above
We then read both integers 1 and 2 using readInt
So what we have demonstrated here is there is no association between the way can read and write data
What that means in above we use java.nio package to write data to file.
But below we use RandomAccessFile from java.io class to read that data and even though we wrote data to bytes above in java.nio package.
But random access file is still able to know what data is String and which one is int and hence we can read it using readInt methods etc.
RandomAccessFile ra = new RandomAccessFile("data.dat","rwd"); byte[]b = new byte[outputBytes.length]; ra.read(b); System.out.println(new String(b)); long int1 = ra.readInt(); long int2 = ra.readInt(); System.out.println(int1); System.out.println(int2); } catch (IOException e){ e.printStackTrace(); } } RandomAccessFile ra = new RandomAccessFile("data.dat","rwd"); FileChannel channel = ra.getChannel(); long numBytesRead = channel.read(buffer);if we do like we did above we will see Hello World printed but please note since we did not use flip the Hello world we are getting is not from the file but from the buffer we defined above i.e this one
ByteBuffer buffer = ByteBuffer.wrap(outputBytes);
int numBytes = binChannel.write(buffer);
To demonstrate this we can even change bytes in our buffer and it will read something like abllo world!
although the data in actual file data.dat is Hello World!
RandomAccessFile ra = new RandomAccessFile("data.dat","rwd");
FileChannel channel = ra.getChannel();
outputBytes[0] ='a';
outputBytes[1] ='b';
long numBytesRead = channel.read(buffer);
System.out.println("Output byte "+new String(outputBytes));
So the correct way of doing this is always call the flip method before switching between reading and writing
as we have done below and in that way even if we add bytes like a ,b to the buffer the correct String
Hello world will be printed as it is reading from the actual file.
RandomAccessFile ra = new RandomAccessFile("data.dat","rwd");
FileChannel channel = ra.getChannel();
outputBytes[0] ='a';
outputBytes[1] ='b';
buffer.flip();
long numBytesRead = channel.read(buffer);
System.out.println("Output byte "+new String(outputBytes));
System.out.println("-------------------------------------------------------------------");
/*------------------------------------------------------------------------------------------------*/
//Now we can comment out the above System.our.println and use the below method to read data as well
if(buffer.hasArray()){
System.out.println(" byte buffer = "+new String(buffer.array()));
}
System.out.println("-------------------------------------------------------------------");
//Then we can use the intBuffer method to read the two integers as well
//Relative read
//Please note that we have to use flip a few times below . before read and before get Int as well.
//To avoid using flip again and again we can use another version of getInt method
//The below method is called Relative read as we have not defined any index to read from
//Example of absolute read is after this example.
intBuffer.flip();
numBytes = channel.read(intBuffer);
intBuffer.flip();
System.out.println(intBuffer.getInt());
intBuffer.flip();
numBytes = channel.read(intBuffer);
intBuffer.flip();
System.out.println(intBuffer.getInt());
channel.close();
ra.close();
//Absolute read
//So in the below method we don't have to use flip method after reading from the file channel as we
//are using the absolute version of getInt method where we are passing index 0.So basically we are telling
//to read from index position 0 of the buffer .
//So we can either flip method or we can use index position.
//in below method we only use flip when we are reading from file channel
// we don't use it when we read from buffer and write to console as we are passing the index from which
//position we want to read from the buffer
intBuffer.flip();
numBytesRead = channel.read(intBuffer);
System.out.println(intBuffer.getInt(0));
intBuffer.flip();
numBytesRead = channel.read(intBuffer);
System.out.println(intBuffer.getInt(0));
channel.close();
ra.close();
/*Below code is only for demonstration purposes . We have to keep in mind that if we use absolute read the
* buffer position is not updated after each read. to demonstrate this lets flip the buffer ,do a absolute read
* and then do a relative read.*/
/* intBuffer.flip();
numBytesRead = channel.read(intBuffer);
System.out.println(intBuffer.getInt(0));
intBuffer.flip();
numBytesRead = channel.read(intBuffer);
// so after read lets flip the buffer as after the absolute read we will be using relative read.
intBuffer.flip();
System.out.println(intBuffer.getInt(0));
//so here we use relative read. So if we run our code it will print the last integer -98765 twice
//th reason is absolute read never changes the buffer position because otherwise the above line of code
//would have taken buffer to end of file and below line would have thrown bufferoverflow error.
//so moral of story is either use absolute read or relative read but never use both otherwise it can lead to
//to many errors.
System.out.println(intBuffer.getInt());
channel.close();
ra.close();
}
catch (IOException e){
e.printStackTrace();
}
}