Streetboy Streetboy - 5 days ago 6
Java Question

Reading files bits and saving them

i have file reader which read entire file and write it's bits.
I have this class which help reading:

import java.io.*;

public class FileReader extends ByteArrayInputStream{

private int bitsRead;
private int bitPosition;
private int currentByte;
private int myMark;
private final static int NUM_BITS_IN_BYTE = 8;
private final static int END_POSITION = -1;
private boolean readingStarted;
/**
* Create a BitInputStream for a File on disk.
*/
public FileReader( byte[] buf ) throws IOException {
super( buf );

myMark = 0;
bitsRead = 0;
bitPosition = NUM_BITS_IN_BYTE-1;
currentByte = 0;
readingStarted = false;
}


/**
* Read a binary "1" or "0" from the File.
*/
public int readBit() throws IOException {
int theBit = -1;

if( bitPosition == END_POSITION || !readingStarted ) {
currentByte = super.read();
bitPosition = NUM_BITS_IN_BYTE-1;
readingStarted = true;
}

theBit = (0x01 << bitPosition) & currentByte;
bitPosition--;

if( theBit > 0 ) {
theBit = 1;
}

return( theBit );
}


/**
* Return the next byte in the File as lowest 8 bits of int.
*/
public int read() {
currentByte = super.read();
bitPosition = END_POSITION;
readingStarted = true;

return( currentByte );
}


/**
*
*/
public void mark( int readAheadLimit ) {
super.mark(readAheadLimit);
myMark = bitPosition;
}


/**
* Add needed functionality to super's reset() method. Reset to
* the last valid position marked in the input stream.
*/
public void reset() {
super.pos = super.mark-1;
currentByte = super.read();
bitPosition = myMark;
}


/**
* Returns the number of bits still available to be read.
*/
public int availableBits() throws IOException {
return( ((super.available() * 8) + (bitPosition + 1)) );
}

}


In class where i call this, i do:

FileInputStream inputStream = new FileInputStream(file);

byte[] fileBits = new byte[inputStream.available()];

inputStream.read(fileBits, 0, inputStream.available());
inputStream.close();

FileReader bitIn = new FileReader(fileBits);


and this work correctly.
However i have problems with big files above 100 mb because byte[] have the end.

So i want to read bigger files. Maybe some could suggest how i can improve this code ?

Thanks.

Answer

If scaling to large file sizes is important, you'd be better off not reading the entire file into memory. The downside is that handling the IOException in more locations can be a little messy. Also, it doesn't look like your application needs something that implements the InputStream API, it just needs the readBit() method. So, you can safely encapsulate, rather than extend, the InputStream.

class FileReader {

  private final InputStream src;

  private final byte[] bits = new byte[8192];

  private int len;

  private int pos;

  FileReader(InputStream src) { 
    this.src = src; 
  }

  int readBit() throws IOException {
    int idx = pos / 8;
    if (idx >= len) {
      int n = src.read(bits);
      if (n < 0)
        return -1;
      len = n;
      pos = 0;
      idx = 0;
    }
    return ((bits[idx] & (1 << (pos++ % 8))) == 0) ? 0 : 1;
  }

}

Usage would look similar.

FileInputStream src = new FileInputStream(file);
try {
  FileReader bitIn = new FileReader(src);
  ...
} finally {
  src.close();
}

If you really do want to read in the entire file, and you are working with an actual file, you can query the length of the file first.

File file = new File(path);
if (file.length() > Integer.MAX_VALUE)
  throw new IllegalArgumentException("File is too large: " + file.length());
int len = (int) file.length();
FileInputStream inputStream = new FileInputStream(file);
try { 
  byte[] fileBits = new byte[len];
  for (int pos = 0; pos < len; ) {
    int n = inputStream.read(fileBits, pos, len - pos);
    if (n < 0)
      throw new EOFException();
    pos += n;
  }
  /* Use bits. */
  ...
} finally {
  inputStream.close();
}
Comments