Tuesday, March 31, 2009

Using Java I/O API to read and write integers as bytes to a binary file

Java I/O API is very vast. I have shown how to use BufferedInputStream and BufferedOutput stream to read and write integers as bytes to a file.My test case only involves 5 integers but the program is meant for a large number of integers.

BufferedInputStream:
My input stream is binary and not character. So I have used the stream type BufferedInputStream. I have wrapped the plain FieInputStream with the BufferedInputStream so that the input can be buffered. Then I have used the following method int read(byte[] b,int off,int len) to read up to len bytes of data from the stream into an array of bytes, thereby, improving performance.

BufferedOutputStream:
I have used the BufferedOutputStream to write all the bytes at once instead of using the plain FileOutputStream.

Efficient way to read integers from a file of bytes

import java.io.*;

public class ReadFile {

private static final int numOfBytesInInt = 4;

/**
* This method reads bytes from a file and converts them to integers.
* @param name filename
* @param numOfBytes Number of bytes to read
* @return Integer array
*/
public static int[] getIntegersStoredAsBytesFromFile(String name, int numOfBytes) throws IOException {
byte[] b = readBytesFromFile(name,numOfBytes);
return convertBytesToInteger(b);
}

/**
* This method actually reads bytes from a file in one shot(hopefully).
* It is more efficient than reading 1 integer or byte at a time in a file.
* @param name filename
* @param numOfBytes Number of bytes to read
*/
public static byte[] readBytesFromFile(String name, int numOfBytes) throws IOException {
BufferedInputStream bis = null;
byte[] b = new byte[numOfBytes];
try {
bis = new BufferedInputStream(new FileInputStream(name));
bis.read(b);
return b;
} finally {
bis.close();
}
}

/**
* This helper method actually converts each 4 bytes into an integer.
* There is also another way of doing it through bit manipulation.
* @param b The byte array to convert to int
* @return The array of ints
*/
public static int[] convertBytesToInteger(byte[] b) throws IOException{
ByteArrayInputStream bai = new ByteArrayInputStream(b);
DataInputStream dis = new DataInputStream(bai);
int len = b.length;
int[] ints = new int[len/numOfBytesInInt]; //4 bytes = 1 int
for (int i = 0,j = 0; i < len; i += numOfBytesInInt, j++) {
ints[j] = dis.readInt();
}
return ints;
}

}


 

Efficient way to write integers as bytes to a file

import java.io.*;
import java.util.Random;

public class WriteFile {

private static final int numOfBytesInInt = 4;

/**
* This method generates integers using random number
* generator, converts each integer to 4 bytes and
* writes it to a file
* @param name filename
* @param numOfBytes Number of bytes to write
*/
public static void generateIntegersAndStoreInFileAsBytes(
String name, int numOfBytes) throws IOException {
//Generate a byte[] of integers
byte[] b = generateIntegersAsBytes(numOfBytes);
//Write them to the file
writeBytesToFile(name,b);
}

/**
* This helper method actually generates the integers
* using random number generator and converts
* each integer to 4 bytes
* @param numOfBytes Number of bytes to generate
* @return Array of bytes
*/
public static byte[] generateIntegersAsBytes(int numOfBytes) throws IOException {
byte[] b = new byte[numOfBytes];
int randomNumber = 0;
Random r = new Random();
for (int offset = 0; offset < b.length; offset += numOfBytesInInt) {
//Generate random int
randomNumber = r.nextInt();
//Just have it here to show what is written into the file
//is what we are reading later..
System.out.println(randomNumber);
//convert int to bytes
intToByte(randomNumber, b, offset);
}
return b;
}

/**
* This method writes all the bytes to a file in a one shot(hopefully without blocking).
* It is more efficient than writing 1 integer at a time in a file.
* @param name filename
* @param b Array of bytes to write
*/
public static void writeBytesToFile(String name, byte[] b) throws IOException{
BufferedOutputStream bos = null;
try {
bos = new BufferedOutputStream(new FileOutputStream(name));
bos.write(b);
bos.flush();
} finally {
bos.close();
}
}

/**
* This helper method actually converts each integer to 4 bytes.
* See the next method for another way to do the same thing.
* @param integer Integer to be converted to bytes
* @param b The byte array into which the bytes have to be stored
* @param offset The start offset in b
*/
public static void intToByte(int integer, byte[] b, int offset) throws IOException {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
DataOutputStream dos = new DataOutputStream(bos);
dos.writeInt(integer);
dos.flush();
System.arraycopy(bos.toByteArray(), 0, b, offset, numOfBytesInInt);
}


/* Instead of using the intToByte method, one could also manually convert
* the integer to an array of bytes using bit arithmetic.
* Here is a way to do it. To use this method, just replace the call
* to the method intToByte() with generateByte() above
*
* private static void generateByte(int integer, byte[] b, int offset) {
b[offset]=(byte)((integer & 0xff000000)>>>24);
b[offset+1]=(byte)((integer & 0x00ff0000)>>>16);
b[offset+2]=(byte)((integer & 0x0000ff00)>>>8);
b[offset+3]=(byte)((integer & 0x000000ff));
}*/

}

 

Test It

import java.io.*;
import static java.lang.System.out;

public class TestingFiles {

private static final String filename = "INPUTBYTES";

/*
* I am just testing with 5 integers but you can practically
* use any number which is a multiple of 4.
*/
private static final int numOfBytes = 20;

public static void main(String[] args) throws IOException{
out.println("Writing to File");
WriteFile.generateIntegersAndStoreInFileAsBytes(filename,numOfBytes);
out.println("Reading from File");
int[] ints = ReadFile.getIntegersStoredAsBytesFromFile(filename,numOfBytes);
for (int i: ints) {
out.println(i);
}
}


}


Here is the output:
Writing to File
931158145
189423544
641759623
-1731070039
1477642147
Reading from File
931158145
189423544
641759623
-1731070039
1477642147

No comments:

Post a Comment