139. Reading/writing binary files efficiently

In the previous problem, Reading/writing text files efficiently, we talked about buffering streaming (for a clear picture, consider reading that problem before this one). Things work the same for binary files too, and so we can jump directly into some examples.

Let's consider the following binary file and its size in bytes:

Path binaryFile = Paths.get(
"build/classes/modern/challenge/Main.class");

int fileSize = (int) Files.readAttributes(
binaryFile, BasicFileAttributes.class).size();

We can read the file's content in a byte[] via FileInputStream (this doesn't use buffering):

final byte[] buffer = new byte[fileSize];
try (InputStream is = new FileInputStream(binaryFile.toString())) {

int i;
while ((i = is.read(buffer)) != -1) {
System.out.print(" Reading ... ");
}
}

However, the preceding example isn't very efficient. Achieving high efficiency when it comes to reading the buffer.length bytes from this input stream into a byte array can be done via BufferedInputStream, as follows:

final byte[] buffer = new byte[fileSize];

try (BufferedInputStream bis = new BufferedInputStream(
new FileInputStream(binaryFile.toFile()))) {

int i;
while ((i = bis.read(buffer)) != -1) {
System.out.print(" Reading ... " + i);
}
}

FileInputStream can be obtained via the Files.newInputStream() method as well. The advantage of this method consists of the fact that it supports Path directly:

final byte[] buffer = new byte[fileSize];

try (BufferedInputStream bis = new BufferedInputStream(
Files.newInputStream(binaryFile))) {

int i;
while ((i = bis.read(buffer)) != -1) {
System.out.print(" Reading ... " + i);
}
}

If the file is too large to fit in a buffer of the file size, then it is preferable to read it via a smaller buffer with a fixed size (for example, 512 bytes) and the read() flavors, which are as follows:

  • read​(byte[] b)
  • read​(byte[] b, int off, int len)
  • readNBytes​(byte[] b, int off, int len)
  • readNBytes​(int len)
The read() method without arguments will read the input stream byte by byte. This is the most inefficient way, especially without using buffering.

Alternatively, if our goal is to read the input stream as a byte array, we can rely on ByteArrayInputStream (it uses an internal buffer, so there is no need to use BufferedInputStream):

final byte[] buffer = new byte[fileSize];

try (ByteArrayInputStream bais = new ByteArrayInputStream(buffer)) {

int i;
while ((i = bais.read(buffer)) != -1) {
System.out.print(" Reading ... ");
}
}

The preceding approaches are a good fit for raw binary data, but sometimes, our binary files contain certain data (for example, ints, floats, and so on). In such cases, DataInputStream and DataOutputStream provide convenient methods for reading and writing certain data types. Let's consider that we have a file, data.bin, that contains float numbers. We can efficiently read it as follows:

Path dataFile = Paths.get("data.bin");

try (DataInputStream dis = new DataInputStream(
new BufferedInputStream(Files.newInputStream(dataFile)))) {

while (dis.available() > 0) {
float nr = dis.readFloat();
System.out.println("Read: " + nr);
}
}
These two classes are just two of the data filters provided by Java. For an overview of all the supported data filters, check out the subclasses of FilterInputStream. Moreover, the Scanner class is a good alternative for reading certain types of data. Check out the problem in the Working with Scanner section for more information.

Now, let's see how we can read binary files directly into memory.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
13.59.48.161