user1224441
user1224441

Reputation:

When does FileReader.read() return -1 and how to handle it?

  1. From https://docs.oracle.com/javase/8/docs/api/?java/io/FileReader.html

    public int read(char[] cbuf) throws IOException

    Reads characters into an array. This method will block until some input is available, an I/O error occurs, or the end of the stream is reached.

    Parameters: cbuf - Destination buffer

    Returns: The number of characters read, or -1 if the end of the stream has been reached

    Throws: IOException - If an I/O error occurs

    What does "the end of the stream has been reached" mean?

    When finishing reading the data in a file, is the end of the stream reached? Then why read returns the number of characters read, instead of -1?

  2. How shall I handle the case when read returns -1? My following code has no checking and handling, and I guess it is not proper.

    File file = new File("Char.txt");                                
    
    try(FileReader fr = new FileReader(file)){
        int[] input = new int[100];
        char[] str = new char[(int) file.length()];
        fr.read(str);                                                                                                              
    
    } catch (IOException e){
        System.err.println(e);
    }
    

Upvotes: 0

Views: 1609

Answers (3)

slim
slim

Reputation: 41223

Readers read through streams of character. This is different from a Java 8 Stream object; don't get them confused.

That is:

  • a sequence of characters one after the other
  • the consumer doesn't know when the end will come, until it reaches it (it's even possible there will never be a defined end!)
  • the consumer cannot go backwards. If you don't handle a character as you see it, you've missed it forever.

It could be a stream of characters from a socket, or from user input, or from some class of your own invention. In the case of FileReader, the stream is the content of the file.

public int read(char[] cbuf) does one of three things, depending on how much is available to read.

  • If it can read enough to fill cbuf, it will fill cbuf and return cbuf.length (that is, the number of chars read in this call)
  • If can read more than 0 chars, but not enough to fill cbuf, it will put as many chars as it can into cbuf, and return that number
    • For a file, this is most likely to happen near the end of the file -- although there are reasons it could happen at any time (e.g. a slow disk, or a really huge array)
    • For other sources, it could happen at any time -- for example, a user has stopped typing
      • If the supplier says the stream is finished, it puts nothing into cbuf and returns -1
      • In the case of a file, that means it's reached the end of the file.

Notice that this never returns 0. Reader promises to block -- that is, wait -- until it can return at least one character, or report end-of-stream.

However other classes also have read(), and read() is also familiar to C programmers. In some other classes (for example ReadableByteChannel), read() can return 0 meaning "There was nothing there, but the stream is still open. If you ask again later there may be data." -- so for consistency with these APIs, Reader.read() uses -1 rather than 0 to indicate end of file.


Hence one of the common ways to work through the contents of a file (or network input, or user input, or any other kind of stream) is:

 try(Reader reader = new FooReader(...)) {

     char[] cbuf = new char[BUFFER_SIZE];
     while(true) {
         int num = reader.read(cbuf);
         if(num == -1) {
             break;
         }
         doSomethingWith(cbuf,num);
     }
 }

doSomethingWith(cbuf,num) should only use elements cbuf[0] to cbuf[num-1].


You should never assume that a single read() will slurp up a whole file. Some classes do it (e.g. DataInputStream.readFully()), but typically read() does not promise to. So this is a bad idea:

Reader reader = new FileReader(file); 
char[] chars = new char[file.length()];
reader.read(chars);
// now chars contains the whole file -- or does it?

It will probably work most of the time for small files on a lightly loaded system. Then one day when you least expect it, read() will only read part of the file. Perhaps someone's submitted a file that's larger than a certain size for the first time. Perhaps the disk system is too busy to supply the whole file in one go on this occasion. read() has done what its documentation promises, but the program breaks.

Upvotes: 1

Brian Agnew
Brian Agnew

Reputation: 272297

Your reader (FileReader) is providing the means of reading characters but underlying that is some form of InputStream providing a sequence of bytes. When that fails to read any more bytes (when it's reached the end of the file), then you'll get a -1 returned.

If your Reader reads a sequence of characters, you'll get that number of characters. You'll get -1 if all characters have been read. e.g. if it reads a 30 character sequence in blocks of 8, you could get the following results from a read() : 8, 8, 8, 6, -1. You'll note that you read all 30 characters (8+8+8+6) and then get an end-of-stream indicator.

Upvotes: 2

d0ge
d0ge

Reputation: 26

What does "the end of the stream has been reached" mean?

In the context of a file, this refers to the end of the file. There is some kind of InputStream reading bytes from the file, and it's done.

When finishing reading the data in a file, is the end of the stream reached? Then why read returns the number of characters read, instead of -1?

Read will return the number of characters read, even if it includes the EOF. If only the EOF is read, then it will return -1.

This implementation allows other functions to know where to store the read data, based on how many bytes was read (incrementing a bytes-read counter, moving the internal character pointer).

It also makes it very easy to code, because as long as the method does not return -1, it contains at least some data to still read.

How shall I handle the case when read returns -1? My following code has no checking and handling, and I guess it is not proper.

Adding the following loop:

while(fr.read(str) != -1) { //code }

to inside your try catch will be sufficient; the read data will be stored in your str object.

Upvotes: -1

Related Questions