I have wrote code that takes an input file text that can be compressed, and should contain ascii values, and then a new file is created with an appended ".lzw" and that file can then be decompressed. I want to know if I can improve this code, e.g. Simplifying buffer read/write bytes using RandomAccessFile.
Compression Class
package lzw;
import java.io.*;
import java.util.*;
public class LZWCompression {
// Define a HashMap and other variables that will be used in the program
public HashMap<String, Integer> dictionary = new HashMap<>();
public int dictSize = 256;
public String str = "";
public byte inputByte;
public byte[] buffer = new byte[3];
public boolean onleft = true;
/**
* Takes in a file name that is uncompressed, and will compress it's file
* contents and append a ".lzw" to the end of the current file name
*
* @param uncompressed - Name of uncompressed file being compressed
* @throws java.io.IOException - File input/output failure
*/
public void compress(String uncompressed) throws IOException {
// Dictionary size limit, builds dictionary
for (int i = 0; i < 256; i++) {
dictionary.put(Character.toString((char) i), i);
}
// Read input uncompress file & Write out compressed file
RandomAccessFile read = new RandomAccessFile(uncompressed, "r");
RandomAccessFile out = new RandomAccessFile(uncompressed.concat(
".lzw"), "rw");
try {
// Reads the First Character from input file into the String
inputByte = read.readByte();
int i = new Byte(inputByte).intValue();
if (i < 0) {
i += 256;
}
char ch = (char) i;
str = "" + ch;
// Reads Character by Character
while (true) {
inputByte = read.readByte();
i = new Byte(inputByte).intValue();
if (i < 0) {
i += 256;
}
System.out.print(i + ", ");
ch = (char) i;
// If str + ch is in the dictionary..
// Set str to str + ch
if (dictionary.containsKey(str + ch)) {
str = str + ch;
} else {
String s12 = to12bit(dictionary.get(str));
// Store the 12 bits into an array and then write it to the
// output file
if (onleft) {
buffer[0] = (byte) Integer.parseInt(
s12.substring(0, 8), 2);
buffer[1] = (byte) Integer.parseInt(
s12.substring(8, 12) + "0000", 2);
} else {
buffer[1] += (byte) Integer.parseInt(
s12.substring(0, 4), 2);
buffer[2] = (byte) Integer.parseInt(
s12.substring(4, 12), 2);
for (int b = 0; b < buffer.length; b++) {
out.writeByte(buffer[b]);
buffer[b] = 0;
}
}
onleft = !onleft;
// Add str + ch to the dictionary
if (dictSize < 4096) {
dictionary.put(str + ch, dictSize++);
}
// Set str to ch
str = "" + ch;
}
}
/**
* Handles input/output file failure by converting 8bit to 12bit
* then storing integers to byte and writing to output file else add
* the buffers to [1] or use buffer[2] then using the length and a
* for loop to output the bytes and then zero out the buffer, note
* this code is similar to above code, which insures bits are stored
*/
} catch (IOException e) {
String str12bit = to12bit(dictionary.get(str));
if (onleft) {
buffer[0] = (byte) Integer.parseInt(str12bit.substring(0, 8), 2);
buffer[1] = (byte) Integer.parseInt(str12bit.substring(8, 12)
+ "0000", 2);
out.writeByte(buffer[0]);
out.writeByte(buffer[1]);
} else {
buffer[1] += (byte) Integer.parseInt(str12bit.substring(0, 4), 2);
buffer[2] = (byte) Integer.parseInt(str12bit.substring(4, 12), 2);
for (int b = 0; b < buffer.length; b++) {
out.writeByte(buffer[b]);
buffer[b] = 0;
}
}
read.close();
out.close();
}
}
/**
* Converts 8 bits to 12 bits
*
* @param i - Integer value
* @return - String value of integer in 12 bit
*/
public String to12bit(int i) {
String str = Integer.toBinaryString(i);
while (str.length() < 12) {
str = "0" + str;
}
return str;
}
/**
* After creating a lzw object scans user input for file to compress, and
* prints out contents of file being compressed along with integer values of
* the characters being compressed, and will return your file name with an
* appended ".lzw"
*
* @param args - The command line arguments
* @throws java.io.IOException - File input/output failure
*/
public static void main(String[] args) throws IOException {
try {
LZWCompression lzw = new LZWCompression();
Scanner input = new Scanner(System.in);
System.out.println("Enter the name of your (input.txt) file.");
String str = input.nextLine();
File file = new File(str);
Scanner fileScanner = new Scanner(file);
String line = "";
while (fileScanner.hasNext()) {
line = fileScanner.nextLine();
System.out.println("Contents of your file being compressed: \n"
+ line);
}
lzw.compress(str);
System.out.println("\nCompression of your file is complete!");
System.out.println("Your new file is named: " + str.concat(".lzw"));
} catch (FileNotFoundException e) {
System.out.println("File was not found!");
}
}
}
Decompression Class
package lzw;
import java.io.*;
import java.util.*;
public class LZWDecompression {
// Define a HashMap and other variables that will be used in the program
public HashMap<Integer, String> dictionary = new HashMap<>();
public String[] Array_char;
public int dictSize = 256;
public int currword;
public int priorword;
public byte[] buffer = new byte[3];
public boolean onleft = true;
/**
* Decompress Method that takes in input, output as a file path Then
* decompress the input to same file as the one passed to compress method
* without loosing any information. In the decompression method it reads in
* 3 bytes of information and write 2 characters corresponding to the bits
* read.
*
* @param input - Name of input file path
* @throws java.io.IOException - File input/output failure
*/
public void LZW_Decompress(String input) throws IOException {
// DictSize builds up to 4k, Array_Char holds these values
Array_char = new String[4096];
for (int i = 0; i < 256; i++) {
dictionary.put(i, Character.toString((char) i));
Array_char[i] = Character.toString((char) i);
}
// Read input as uncompressed file & Write out compressed file
RandomAccessFile in = new RandomAccessFile(input, "r");
RandomAccessFile out = new RandomAccessFile(input.replace(
".lzw", ""), "rw");
try {
// Gets the first word in code and outputs its corresponding char
buffer[0] = in.readByte();
buffer[1] = in.readByte();
priorword = getvalue(buffer[0], buffer[1], onleft);
onleft = !onleft;
out.writeBytes(Array_char[priorword]);
// Reads every 3 bytes and generates corresponding characters
while (true) {
if (onleft) {
buffer[0] = in.readByte();
buffer[1] = in.readByte();
currword = getvalue(buffer[0], buffer[1], onleft);
} else {
buffer[2] = in.readByte();
currword = getvalue(buffer[1], buffer[2], onleft);
}
onleft = !onleft;
if (currword >= dictSize) {
if (dictSize < 4096) {
Array_char[dictSize] = Array_char[priorword]
+ Array_char[priorword].charAt(0);
}
dictSize++;
out.writeBytes(Array_char[priorword]
+ Array_char[priorword].charAt(0));
} else {
if (dictSize < 4096) {
Array_char[dictSize] = Array_char[priorword]
+ Array_char[currword].charAt(0);
}
dictSize++;
out.writeBytes(Array_char[currword]);
}
priorword = currword;
}
} catch (EOFException e) {
in.close();
out.close();
}
}
/**
* Extract the 12 bit key from 2 bytes and gets the integer value of the key
*
* @param b1 - First byte
* @param b2 - Second byte
* @param onleft - True if on left, false if not
* @return - An Integer which holds the value of the key
*/
public int getvalue(byte b1, byte b2, boolean onleft) {
String temp1 = Integer.toBinaryString(b1);
String temp2 = Integer.toBinaryString(b2);
while (temp1.length() < 8) {
temp1 = "0" + temp1;
}
if (temp1.length() == 32) {
temp1 = temp1.substring(24, 32);
}
while (temp2.length() < 8) {
temp2 = "0" + temp2;
}
if (temp2.length() == 32) {
temp2 = temp2.substring(24, 32);
}
if (onleft) {
return Integer.parseInt(temp1 + temp2.substring(0, 4), 2);
} else {
return Integer.parseInt(temp1.substring(4, 8) + temp2, 2);
}
}
/**
* After creating a lzw object scans user input for file to compress, and
* prints out contents of file being compressed along with integer values of
* the characters being compressed, and will return your file name with an
* appended ".lzw"
*
* @param args - The command line arguments
* @throws java.io.IOException - File input/output failure
*/
public static void main(String[] args) throws IOException {
try {
LZWDecompression lzw = new LZWDecompression();
Scanner input = new Scanner(System.in);
System.out.println("Enter the name of your (input.txt.lzw) file.");
String str = input.nextLine();
File file = new File(str);
Scanner fileScanner = new Scanner(file);
String line = "";
while (fileScanner.hasNext()) {
line = fileScanner.nextLine();
System.out.println("Contents of your file being decompressed:\n"
+ line);
}
lzw.LZW_Decompress(str);
System.out.println("Decompression of your file is complete!");
System.out.println("Your new file is named: "
+ str.replace(".lzw", ""));
} catch (FileNotFoundException e) {
System.out.println("File was not found!");
}
}
}
Some versions of LZW compression are copyrighted. Since a couple of year, their owner (Unisys) demands royalties from any company using their algorithm.
\$\endgroup\$