210 lines
7.9 KiB
Java
210 lines
7.9 KiB
Java
import java.io.*;
|
|
import java.util.*;
|
|
|
|
public class HuffmanCompressor {
|
|
private String filename;
|
|
|
|
public static final int CHAR_MAX = 255; // max char value
|
|
public static final int MAKE_CODE = 1;
|
|
public static final int COMPRESS = 2;
|
|
public static final int DECOMPRESS = 3;
|
|
public static final int ROUND_TRIP = 4;
|
|
|
|
public HuffmanCompressor(String filename) {
|
|
if (!filename.endsWith(".txt")) {
|
|
throw new IllegalArgumentException(
|
|
"This compressor only works on text files!");
|
|
}
|
|
this.filename = filename.split(".txt")[0];
|
|
}
|
|
|
|
public void makeCode() throws IOException {
|
|
System.out.println("I am about to make the Huffman code for "
|
|
+ filename + ".txt...");
|
|
FileInputStream input = new FileInputStream(filename + ".txt");
|
|
int[] count = new int[CHAR_MAX];
|
|
int n = input.read();
|
|
while (n != -1) {
|
|
int prev = n;
|
|
count[n]++;
|
|
n = input.read();
|
|
|
|
// Remove added newline by ed
|
|
if (n == -1 && prev == 10) {
|
|
count[prev]--;
|
|
}
|
|
}
|
|
|
|
// Build the code; open the output file; save the code
|
|
System.out.println("\tI built up a frequency table of the " +
|
|
"characters in your file.");
|
|
System.out.println("\tNow, I'm going to call your HuffmanCode(int[]) " +
|
|
"constructor using that frequency table.");
|
|
HuffmanCode t = new HuffmanCode(count);
|
|
|
|
System.out.println("\tOkay! Now, I am going to save the code (using " +
|
|
"your save method) to the file " + filename +
|
|
".code!");
|
|
PrintStream output = new PrintStream(new File(filename + ".code"));
|
|
t.save(output);
|
|
System.out.println("...I am done making the Huffman Code!");
|
|
}
|
|
|
|
public void compress(boolean debug) throws IOException {
|
|
System.out.println("I am about to attempt to COMPRESS "
|
|
+ filename + ".txt:");
|
|
/* We must make the code before we can compress... */
|
|
this.makeCode();
|
|
System.out.println("\tNow that I have the Huffman Code, I am going " +
|
|
"to use the huffman code file created by");
|
|
System.out.println("\tyour save() method to compress the contents " +
|
|
"into " + filename + ".short!");
|
|
|
|
String[] codes = new String[CHAR_MAX];
|
|
Scanner codeInput = new Scanner(new File(filename + ".code"));
|
|
while (codeInput.hasNextLine()) {
|
|
int n = Integer.parseInt(codeInput.nextLine());
|
|
codes[n] = codeInput.nextLine();
|
|
}
|
|
|
|
// Open file to be compressed; open output file
|
|
FileInputStream input = new FileInputStream(this.filename + ".txt");
|
|
BitOutputStream output = new BitOutputStream(
|
|
new PrintStream(this.filename + ".short"), debug);
|
|
|
|
// Print out the debug binary to a .debug file
|
|
FileOutputStream debugOut = null;
|
|
if (debug) {
|
|
debugOut = new FileOutputStream(this.filename + ".debug");
|
|
}
|
|
|
|
// Do the compression
|
|
int n = input.read();
|
|
while (n != -1) {
|
|
if (codes[n] == null) {
|
|
// check if this is actually an error, or an ed added newline
|
|
if (!(n == 10 && input.read() == -1)) {
|
|
System.out.println("Your code file has no code for " + n +
|
|
" (the character '" + (char) n + "')");
|
|
System.out.println("exiting...");
|
|
System.exit(1);
|
|
}
|
|
n = -1;
|
|
} else {
|
|
for (int i = 0; i < codes[n].length(); i++) {
|
|
output.write(codes[n].charAt(i) - '0');
|
|
// Write to the .debug file
|
|
if (debugOut != null) {
|
|
debugOut.write(codes[n].charAt(i));
|
|
}
|
|
}
|
|
n = input.read();
|
|
}
|
|
}
|
|
input.close();
|
|
output.close();
|
|
System.out.println("...I am done compressing the file");
|
|
}
|
|
|
|
public void decompress(boolean printToConsole) throws IOException {
|
|
System.out.println("I am about to attempt to DECOMPRESS " +
|
|
filename + ".short:");
|
|
|
|
System.out.println("\tTo do this, I must first read in the huffman " +
|
|
"code used to compress the file.");
|
|
System.out.println("\tI will use your HuffmanCode(Scanner) " +
|
|
"constructor!");
|
|
// Open code file and construct tree
|
|
Scanner codeInput = new Scanner(new File(this.filename + ".code"));
|
|
HuffmanCode t = new HuffmanCode(codeInput);
|
|
|
|
// Open compressed file; open output
|
|
BitInputStream input = new BitInputStream(this.filename + ".short");
|
|
PrintStream output = System.out;
|
|
if (!printToConsole) {
|
|
System.out.println("\tNow, I will decompress the file using your " +
|
|
"translate() method and save");
|
|
System.out.println("\tthe output into " + this.filename + ".new");
|
|
output = new PrintStream(new File(this.filename + ".new"));
|
|
}
|
|
else {
|
|
System.out.println("\tNow, I will decompress the file using your " +
|
|
"translate() method and display it on the console");
|
|
}
|
|
|
|
// Decompress the file
|
|
t.translate(input, output);
|
|
output.close();
|
|
System.out.println("...I am done decompressing the file");
|
|
}
|
|
|
|
public static void main(String[] args) throws IOException {
|
|
Scanner console = new Scanner(System.in);
|
|
|
|
System.out.println("Welcome to the CSE 123 Huffman Compressor!");
|
|
System.out.println();
|
|
|
|
String filename;
|
|
|
|
do {
|
|
System.out.print("Which file would you like to work with (it " +
|
|
"must be a txt file)? ");
|
|
filename = console.nextLine().trim();
|
|
} while (!filename.endsWith(".txt"));
|
|
|
|
System.out.println();
|
|
|
|
int choice = -1;
|
|
|
|
while (choice == -1) {
|
|
System.out.println("Would you like to:");
|
|
System.out.println("\t(1) make a huffman code,");
|
|
System.out.println("\t(2) compress a file,");
|
|
System.out.println("\t(3) decompress a file, or");
|
|
System.out.println(
|
|
"\t(4) do a compression followed by a decompression");
|
|
System.out.print("1-4? ");
|
|
String choiceStr = console.nextLine().trim();
|
|
try {
|
|
choice = Integer.parseInt(choiceStr);
|
|
if (choice < 1 || choice > 4) {
|
|
choice = -1;
|
|
}
|
|
} catch(NumberFormatException e) {
|
|
/* Don't change choice; so, the loop will repeat. */
|
|
}
|
|
}
|
|
|
|
boolean debug = false;
|
|
if (choice == COMPRESS || choice == ROUND_TRIP) {
|
|
debug = prompt(
|
|
console,
|
|
"Would you like to debug the compressed file (y/n)? ");
|
|
}
|
|
|
|
boolean toConsole = false;
|
|
if (choice == DECOMPRESS || choice == ROUND_TRIP) {
|
|
toConsole = !prompt(
|
|
console,
|
|
"Would you like to print the result to a file (y/n)? ");
|
|
}
|
|
|
|
HuffmanCompressor huffman = new HuffmanCompressor(filename);
|
|
switch (choice) {
|
|
case 1: huffman.makeCode(); break;
|
|
case 2: huffman.compress(debug); break;
|
|
case 3: huffman.decompress(toConsole); break;
|
|
case 4:
|
|
huffman.compress(debug);
|
|
huffman.decompress(toConsole);
|
|
break;
|
|
}
|
|
}
|
|
|
|
public static boolean prompt(Scanner console, String message) {
|
|
System.out.print(message);
|
|
return console.nextLine().trim().toLowerCase().startsWith("y");
|
|
|
|
}
|
|
}
|