Search code examples
javaiostream

Why doesn't InputStream fill the array fully?


Dude, I'm using following code to read up a large file(2MB or more) and do some business with data.
I have to read 128Byte for each data read call.
At the first I used this code(no problem,works good).

InputStream is;//= something...
int read=-1;
byte[] buff=new byte[128];
while(true){
 for(int idx=0;idx<128;idx++){
  read=is.read(); if(read==-1){return;}//end of stream
  buff[idx]=(byte)read;
 }
 process_data(buff);
}

Then I tried this code which the problems got appeared(Error! weird responses sometimes)

InputStream is;//= something...
int read=-1;
byte[] buff=new byte[128];
while(true){
 //ERROR! java doesn't read 128 bytes while it's available
 if((read=is.read(buff,0,128))==128){process_data(buff);}else{return;}
}

The above code doesn't work all the time, I'm sure that number of data is available, but reads(read) 127 or 125, or 123, sometimes. what is the problem?
I also found a code for this to use DataInputStream#readFully(buff:byte[]):void which works too, but I'm just wondered why the seconds solution doesn't fill the array data while the data is available.
Thanks buddy.


Solution

  • Consulting the javadoc for FileInputStream (I'm assuming since you're reading from file):

    Reads up to len bytes of data from this input stream into an array of bytes. If len is not zero, the method blocks until some input is available; otherwise, no bytes are read and 0 is returned.

    The key here is that the method only blocks until some data is available. The returned value gives you how many bytes was actually read. The reason you may be reading less than 128 bytes could be due to a slow drive/implementation-defined behavior.

    For a proper read sequence, you should check that read() does not equal -1 (End of stream) and write to a buffer until the correct amount of data has been read.

    Example of a proper implementation of your code:

    InputStream is; // = something...
    
    int read;
    int read_total;
    
    byte[] buf = new byte[128];
    
    // Infinite loop    
    while(true){
        read_total = 0;
    
        // Repeatedly perform reads until break or end of stream, offsetting at last read position in array
        while((read = is.read(buf, read_total, buf.length - offset)) != -1){
            // Gets the amount read and adds it to a read_total variable.
            read_total = read_total + read;
            
            // Break if it read_total is buffer length (128)
            if(read_total == buf.length){
                break;
            }
        }
        
        if(read_total != buf.length){
            // Incomplete read before 128 bytes
        }else{
            process_data(buf);
        }
    }
    

    Edit:

    Don't try to use available() as an indicator of data availability (sounds weird I know), again the javadoc:

    Returns an estimate of the number of remaining bytes that can be read (or skipped over) from this input stream without blocking by the next invocation of a method for this input stream. Returns 0 when the file position is beyond EOF. The next invocation might be the same thread or another thread. A single read or skip of this many bytes will not block, but may read or skip fewer bytes.

    In some cases, a non-blocking read (or skip) may appear to be blocked when it is merely slow, for example when reading large files over slow networks.

    The key there is estimate, don't work with estimates.