This repository has been archived on 2026-05-20. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
CSE-123/p3/HuffmanCompressor.java
2026-05-20 16:20:45 -07:00

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");
}
}