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.
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.