Question

I'm trying to understand how inputstreams work. The following block of code is one of the many ways to read data from a text file:-

File file = new File("./src/test.txt");
InputStream input = new BufferedInputStream (new FileInputStream(file));
int data = 0;

while (data != -1)   (-1 means we reached the end of the file)
 {
     data = input.read(); //if a character was read, it'll be turned to a bite and we get the integer representation of it so a is 97 b is 98
     System.out.println(data + (char)data); //this will print the numbers followed by space then the character
 }

input.close();

Now to use input.read(byte, offset, length) i have this code. I got it from here

File file = new File("./src/test.txt");
InputStream input = new BufferedInputStream (new FileInputStream(file));
int totalBytesRead = 0, bytesRemaining, bytesRead;
byte[] result = new byte[ ( int ) file.length()];

while ( totalBytesRead < result.length )
 {
     bytesRemaining = result.length - totalBytesRead;
     bytesRead = input.read ( result, totalBytesRead, bytesRemaining );
     if ( bytesRead > 0 )
      totalBytesRead = totalBytesRead + bytesRead;

     //printing integer version of bytes read
     for (int i = 0; i < bytesRead; i++)
      System.out.print(result[i] + " ");

     System.out.println();

     //printing character version of bytes read
     for (int i = 0; i < bytesRead; i++)
      System.out.print((char)result[i]);
 }

input.close();

I'm assuming that based on the name BYTESREAD, this read method is returning the number of bytes read. In the documentation, it says that the function will try to read as many as possible. So there might be a reason why it wouldn't.

My first question is: What are these reasons?

I could replace that entire while loop with one line of code: input.read(result, 0, result.length)

I'm sure the creator of the article thought about this. It's not about the output because I get the same output in both cases. So there has to be a reason. At least one. What is it?

Was it helpful?

Solution

The documentation of read(byte[],int,int says that it:

  • Reads up to len bytes of data.
  • An attempt is made to read as many as len bytes
  • A smaller number may be read.

Since we are working with files that are right there in our hard disk, it seems reasonable to expect that the attempt will read the whole file, but input.read(result, 0, result.length) is not guaranteed to read the whole file (it's not said anywhere in the documentation). Relying in undocumented behaviors is a source for bugs when the undocumented behavior change.

For instance, the file stream may be implemented differently in other JVMs, some OS may impose a limit on the number of bytes that you may read at once, the file may be located in the network, or you may later use that piece of code with another implementation of stream, which doesn't behave in that way.

Alternatively, if you are reading the whole file in an array, perhaps you could use DataInputStream.readFully

About the loop with read(), it reads a single byte each time. That reduces performance if you are reading a big chunk of data, since each call to read() will perform several tests (has the stream ended? etc) and may ask the OS for one byte. Since you already know that you want file.length() bytes, there is no reason for not using the other more efficient forms.

OTHER TIPS

Imagine you are reading from a network socket, not from a file. In this case you don't have any information about the total amount of bytes in the stream. You would allocate a buffer of fixed size and read from the stream in a loop. During one iteration of the loop you can't expect there are BUFFERSIZE bytes available in the stream. So you would fill the buffer as much as possible and iterate again, until the buffer is full. This can be useful, if you have data blocks of fixed size, for example serialized object.

ArrayList<MyObject> list = new ArrayList<MyObject>();

try {

    InputStream input = socket.getInputStream();
    byte[] buffer = new byte[1024];

    int bytesRead;
    int off = 0;
    int len = 1024;

    while(true) {
        bytesRead = input.read(buffer, off, len);            

        if(bytesRead == len) {
                list.add(createMyObject(buffer));
                // reset variables
                off = 0;
                len = 1024;
                continue;
        }

        if(bytesRead == -1) break;

        // buffer is not full, adjust size
        off += bytesRead;
        len -= bytesRead;
    }

} catch(IOException io) {
    // stream was closed
}

ps. Code is not tested and should only point out, how this function can be useful.

You specify the amount of bytes to read because you might not want to read the entire file at once or maybe you couldn't or might not want to create a buffer as large as the file.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top