101 lines
3.3 KiB
Java
101 lines
3.3 KiB
Java
// The BitOutputStream and BitInputStream classes provide the ability to
|
|
// write and read individual bits to a file in a compact form. One major
|
|
// limitation of this approach is that the resulting file will always have
|
|
// a number of bits that is a multiple of 8. In effect, whatever bits are
|
|
// output to the file are padded at the end with 0's to make the total
|
|
// number of bits a multiple of 8.
|
|
//
|
|
// BitInputStream has the following public methods:
|
|
// public BitInputStream(String file)
|
|
// opens an input stream with the given file name
|
|
// public int nextBit()
|
|
// reads the next bit from input (throws exception if at end of file)
|
|
// public boolean hasNextBit()
|
|
// returns true if there's another bit in the input stream to be read
|
|
// public void close()
|
|
// closes the input
|
|
|
|
import java.io.*;
|
|
import java.util.*;
|
|
|
|
@SuppressWarnings("deprecation")
|
|
public class BitInputStream {
|
|
private FileInputStream input;
|
|
private int currentByte; // current set of bits (buffer)
|
|
private int nextByte; // next set of bits (buffer)
|
|
private int numBits; // how many bits from buffer have been used
|
|
private int remainingAtEnd; // how many bits will be remaining at the end
|
|
// after we're done
|
|
|
|
private static final int BYTE_SIZE = 8; // bits per byte
|
|
|
|
// pre : given file name is legal
|
|
// post: creates a BitInputStream reading input from the file
|
|
public BitInputStream(String file) {
|
|
try {
|
|
this.input = new FileInputStream(file);
|
|
|
|
// Read in the number of remaining bits at the end
|
|
this.remainingAtEnd = this.input.read();
|
|
|
|
// Set up the nextByte field.
|
|
this.nextByte = this.input.read();
|
|
} catch (IOException ex) {
|
|
throw new RuntimeException(ex.toString());
|
|
}
|
|
|
|
this.nextByte();
|
|
}
|
|
|
|
public boolean hasNextBit() {
|
|
boolean atEnd = this.currentByte == -1;
|
|
boolean onlyRemaining = this.nextByte == -1
|
|
&& BYTE_SIZE - this.numBits == this.remainingAtEnd;
|
|
return !atEnd && !onlyRemaining;
|
|
}
|
|
|
|
// post: reads next bit from input
|
|
// throws NoSuchElementException if there is no bit to return
|
|
public int nextBit() {
|
|
// if at eof, throw exception
|
|
if (!this.hasNextBit()) {
|
|
throw new NoSuchElementException();
|
|
}
|
|
int result = this.currentByte % 2;
|
|
this.currentByte /= 2;
|
|
this.numBits++;
|
|
if (this.numBits == BYTE_SIZE) {
|
|
this.nextByte();
|
|
}
|
|
return result;
|
|
}
|
|
|
|
// post: refreshes the internal buffer with the next BYTE_SIZE bits
|
|
private void nextByte() {
|
|
this.currentByte = this.nextByte;
|
|
if (this.currentByte != -1) {
|
|
try {
|
|
this.nextByte = this.input.read();
|
|
} catch (IOException e) {
|
|
throw new RuntimeException(e.toString());
|
|
}
|
|
}
|
|
|
|
this.numBits = 0;
|
|
}
|
|
|
|
// post: input is closed
|
|
public void close() {
|
|
try {
|
|
this.input.close();
|
|
} catch (IOException e) {
|
|
throw new RuntimeException(e.toString());
|
|
}
|
|
}
|
|
|
|
// included to ensure that the stream is closed
|
|
protected void finalize() {
|
|
this.close();
|
|
}
|
|
}
|