Search code examples
javafileinputstream

How to get EOFException from FileInputStream


I am making the project about data compression(using Huffman Algorithm). The project is still in revision. I have run into very interesting problem. I need to read byte by byte from a binary file. I have got this file FileInputHelper that realize few methods:

import java.io.IOException;

public class FileInputHelper implements Closeable {
    private FileInputStream fileInputStream;
    private BufferedReader fileBufferedReader;

    public FileInputHelper(File file) throws IOException {
        fileInputStream = new FileInputStream(file);
        fileBufferedReader = new BufferedReader(
               new InputStreamReader(fileInputStream));
    }


    public byte readByte() throws IOException {
        return (byte)fileInputStream.read();
    }

    public char read() throws IOException {
        return (char)fileInputStream.read();
    }

    public String readLine() throws IOException {
        return fileBufferedReader.readLine();
    }

    @Override
    public void close() throws IOException{
        fileInputStream.close();
    }
}

But when the binary file end, method should return -1. Of course, it should be so. But there is some tests, where there are bytes, that are equal -1, but are not the last. As you understand, this is really critical. If in mid I read -1, I will think that there is end of the file. But it is not. Is there any ways to solve this problem? Can I get EOFException? And if my code is bad, I would like to listen your advice.


Solution

  • The problem is that char is unsigned, while byte is signed. Basically, where is a specific character (0xffff) which maps to -1 when converted to a byte. This is also why the read() method on InputStream returns an integer, even though you are getting a byte or char.

    One way to handle this issue is to check if read() returned -1 before converting it to a byte or char. Then, if read() does return -1, you can throw an EOFException, and catch that. e.g.,

    int cur = fileInputStream.read();
    if(cur == -1) {
        throw new EOFException("End of input reached");
    }else {
        return (char) cur;
    }
    

    However, catching exceptions is not intended as a way to indicate that an operation has completed normally. One way to handle this that avoids this is to buffer a character / byte, and add an available() method, as below.

    public class FileInputHelper implements Closeable {
        private FileInputStream fileInputStream;
        private BufferedReader fileBufferedReader;
        private int next;
    
        public FileInputHelper(File file) throws IOException {
            fileInputStream = new FileInputStream(file);
            fileBufferedReader = new BufferedReader(
                   new InputStreamReader(fileInputStream));
            next = fileInputStream.read();
        }
    
    
        public byte readByte() throws IOException {
            int cur = next;
            next = fileInputStream.read();
            if(cur == -1) {
                throw new IOException("End of file reached");
            }
            return (byte) cur;
        }
    
        public char read() throws IOException {
            int cur = next;
            next = fileInputStream.read();
            if(cur == -1) {
                throw new IOException("End of file reached");
            }
            return (char) cur;
        }
    
        public String readLine() throws IOException {
            return fileBufferedReader.readLine();
        }
    
        @Override
        public void close() throws IOException{
            fileInputStream.close();
        }
    
        // Returns true if there are more chars / bytes to read.
        public boolean available() {
            return next != -1;
        }
    
    }
    

    This will have issues with how the file is read, if you use both the read()/readByte() and readLine() methods, so keep that in mind.