Java学习笔记-基于霍夫曼编码的文本文件压缩与解压缩

关于霍夫曼树与霍夫曼编码的相关知识在另一篇博文中有较为详细的阐述,详情请移步前文

本篇侧重代码干货,实现涉及到很多的类的操作,想查看更多请移步我的GitHub

二叉树节点类,提供对于节点的增删改查相关操作

package Code;

public class TreeNode {
  private Object item;
  private TreeNode leftChild;
  private TreeNode rightChild;

  public TreeNode(Object newItem) {
  // Initializes tree node with item and no children.
    item = newItem;
    leftChild  = null;
    rightChild = null;
  }  // end constructor

  public TreeNode(Object newItem,
                  TreeNode left, TreeNode right) {
  // Initializes tree node with item and
  // the left and right children references.
    item = newItem;
    leftChild  = left;
    rightChild = right;
  }  // end constructor

  public Object getItem() {
  // Returns the item field.
    return item;
  }  // end getItem

  public void setItem(Object newItem) {
  // Sets the item field to the new value newItem.
  item  = newItem;
  }  // end setItem

  public TreeNode getLeft() {
  // Returns the reference to the left child.
    return leftChild;
  }  // end getLeft

  public void setLeft(TreeNode left) {
  // Sets the left child reference to left.
    leftChild  = left;
  }  // end setLeft

  public TreeNode getRight() {
  // Returns the reference to the right child.
    return rightChild;
  }  // end getRight

  public void setRight(TreeNode right) {
  // Sets the right child reference to right.
    rightChild  = right;
  }  // end setRight


  public String toString() {
	  return "" + item;
  }

  public boolean isLeaf() {
    return ((getLeft() == null) && (getRight() == null));
  }

}  // end TreeNode

二叉树基类,提供基本的二叉树操作

package Code;

public abstract class BinaryTreeBasis {
  protected TreeNode root;

  public BinaryTreeBasis() {
    root = null;
  }  // end default constructor

  public BinaryTreeBasis(Object rootItem) {
    root = new TreeNode(rootItem, null, null);
  }  // end constructor

  public boolean isEmpty() {
// Returns true if the tree is empty, else returns false.
    return root == null;
  }  // end isEmpty

  public void makeEmpty() {
// Removes all nodes from the tree.
    root = null;
  }  // end makeEmpty

  public Object getRootItem() throws TreeException {
// Returns the item in the tree�s root.
    if (root == null) {
      throw new TreeException("TreeException: Empty tree");
    }
    else {
      return root.getItem();
    }  // end if
  }  // end getRootItem

  public TreeNode getRoot() throws TreeException {
// additional method to return the root as TreeNode.
      return root;
  }  // end getRoot

}  // end BinaryTreeBasis

继承二叉树基类构建的霍夫曼树/最优二叉树

package Code;

public class BinaryTree extends BinaryTreeBasis {
  public BinaryTree() {
  }  // end default constructor

  public BinaryTree(Object rootItem) {
    super(rootItem);
  }  // end constructor

  public BinaryTree(Object rootItem, 
                    BinaryTree leftTree, 
                    BinaryTree rightTree) {
    root = new TreeNode(rootItem, null, null);
    attachLeftSubtree(leftTree);
    attachRightSubtree(rightTree);
  }  // end constructor

  public void setRootItem(Object newItem) {
    if (root != null) {
      root.setItem(newItem);
    }
    else {
      root = new TreeNode(newItem, null, null);
    }  // end if
  }  // end setRootItem

  public void attachLeft(Object newItem) {
    if (!isEmpty() && root.getLeft() == null) {
      // assertion: nonempty tree; no left child
      root.setLeft(new TreeNode(newItem, null, null));
    }  // end if
  }  // end attachLeft

  public void attachRight(Object newItem) {
    if (!isEmpty() && root.getRight() == null) {
      // assertion: nonempty tree; no right child
      root.setRight(new TreeNode(newItem, null, null));
    }  // end if
  }  // end attachRight

  public void attachLeftSubtree(BinaryTree leftTree) 
                                throws TreeException {
    if (isEmpty()) {
      throw new TreeException("TreeException:  Empty tree");
    }
    else if (root.getLeft() != null) {
      // a left subtree already exists; it should have been 
      // deleted first
      throw new TreeException("TreeException: " + 
                           "Cannot overwrite left subtree");
    }
    else {
      // assertion: nonempty tree; no left child
      root.setLeft(leftTree.root);
      // don't want to leave multiple entry points into 
      // our tree
      leftTree.makeEmpty(); 
    }  // end if
  }  // end attachLeftSubtree

  public void attachRightSubtree(BinaryTree rightTree)  
                                 throws TreeException {
    if (isEmpty()) {
      throw new TreeException("TreeException:  Empty tree");
    }
    else if (root.getRight() != null) {
      // a right subtree already exists; it should have been 
      // deleted first
      throw new TreeException("TreeException: " + 
                          "Cannot overwrite right subtree");
    }
    else {
      // assertion: nonempty tree; no right child
      root.setRight(rightTree.root);
      // don't want to leave multiple entry points into 
      // our tree
      rightTree.makeEmpty(); 
    }  // end if
  }  // end attachRightSubtree
  
  protected BinaryTree(TreeNode rootNode) {
    root = rootNode;
  }  // end protected constructor

  public BinaryTree detachLeftSubtree()  
                         throws TreeException {
    if (isEmpty()) {
      throw new TreeException("TreeException:  Empty tree");
    }
    else {
      // create a new binary tree that has root's left 
      // node as its root
      BinaryTree leftTree;
      leftTree = new BinaryTree(root.getLeft());
      root.setLeft(null);
      return leftTree;
    }  // end if
  }  // end detachLeftSubtree

  public BinaryTree detachRightSubtree() 
                         throws TreeException {
    if (isEmpty()) {
      throw new TreeException("TreeException:  Empty tree");
    }
    else {
      BinaryTree rightTree;
      rightTree = new BinaryTree(root.getRight());
      root.setRight(null);
      return rightTree;
    }  // end if
  }  // end detachRightSubtree

} // end BinaryTree

二叉树异常类(养成良好的编程习惯~)

package Code;

public class TreeException extends RuntimeException {
  public TreeException(String s) {
    super(s);
  }  // end constructor
} // end TreeException

字符权重类,用以得到字符的频率即节点权重

package Code;// This class represents an object which stores a single character and an integer frequency

public class CharFreq implements Comparable {

	private char c;
	private int freq;

	public CharFreq(char c, int freq) {
		this.c = c;
		this.freq = freq;
	}

	public char getChar() {
		return c;
	}

	public int getFreq() {
		return freq;
	}

	public int compareTo(Object o) {
		return freq - ((CharFreq)o).freq;
	}

	public String toString() {
		return "(" + c + ":" + freq + ")";
	}
}

文件流处理类,包括写数据流和读数据流两种操作,应用了前人提供的工具类

package Code;

import java.io.*;
/**
 * A simple class that allows a user to write a stream of bits to a file.
 * You should make sure you .flush() the BitWriter when you have finished writing.
 * Otherwise there will be bits and pieces left in the writer. :)
 * Once you HAVE flushed this writer, you can no longer write bits to it.
 * This is because it would make the algorithm a little more difficult and I am lazy. (it is
 * also not needed)
 *
 * For those that are interested:
 * The bots are stored in sections of bytes. The last byte in the file stores
 * the number of bits in the last byte that were filled in to make up a whole byte.
 *
 *
 * @author Shane Paul
 * 7/10/2003
 * @
 */
public class BitWriter {
	//For writing to a file
	FileOutputStream output;
	//Stores 8 bits, before writing them to the FileWriter
	int buffer;
	//Count the number of bits
	int count;
	//Whether or not this Writer has been closed yet
	boolean closed = false;
	/**
	 * Constuct a BitWriter using a String filename. The file will be overwritten.
	 * @param filename The name of the file.
	 * @exception IOException is thrown if the file does not exist or is not writable
	 */
	public BitWriter(String filename) throws IOException{
		this(new File(filename)); //uses "this" to call the file constructor
	}
	/**
	 * Construct a BitWriter using a File.
	 * @param file The name of the file.
	 * @exception IOException is thrown if the file does not exist or is not writable
	 */
	public BitWriter(File file) throws IOException{
		//Test to see file is actually a file and can have data written to it
		file.createNewFile();
		if(!file.canWrite())
			throw new IOException("Oh My!! This file cannot be written to.....whatever shall I do? \nfile: "+file);
		output = new FileOutputStream(file);
	}
	/**
	 * Writes a single bit to a file. The bit is defined by the boolean it receives
	 * @param bit TRUE = 1, FALSE = 0
	 * @exception IOException Throws an IOException if there were any problems writing to this file
	 */
	public void writeBit(boolean bit)throws Exception{
		//Add in the specified bit by using the other method
		if(bit)
			writeBit(1);
		else
			writeBit(0);
	}
	/**
	 * Flushes the buffer and closes this writer for good. The writer will accept no further bits after this method
	 * has been called. You cannot "flush" this buffer because you cannot write single bits to the file.
	 * Once you have "flushed" this buffer, it is then closed for good. (see class descrip. for why)
	 */
	public void close()throws Exception{
		if(closed)
			throw new BitWriterClosedAlreadyException();
		output.flush();
		if(count!=0){
			int leftOverBits = 8-count;
			buffer = buffer<<leftOverBits;
			output.write(buffer);
			output.write(leftOverBits);
		}
		else
			output.write(0); //no extra bits
		output.close();
	}
	/**
	 * Another method that writes a single bit to a file.
	 * This time it takes in an integer (or you could just pass a byte and it will be upcast)
	 * only values 0 and 1 are acceptable.
	 * <b>You need not do anything special with these exceptions, they are simply to inform you of the type
	 * of error you have so you can debuig easier.</b>
	 * If you try passing it another value, it will throw an exception. (to help debugging)
	 * @param bit
	 * @exception IOException Throws an IOException if there were any problems writing to this file
	 * @exception InvalidBitException An exception I made up to indicate that you are not using this method correctly..
	 */
	public void writeBit(int bit)throws Exception{
		//can't write to a closed bitwriter
		if(closed)
			throw new BitWriterClosedAlreadyException();
		if(bit <0 || bit>1)
			throw new InvalidBitException();
		count++;
		buffer=(buffer<<1);
		//Add in the specified bit
		if(bit==1)
			buffer|=1;
		//empty the buffer and reset the count
		if(count==8){
			output.write(buffer);
			count = 0;buffer = 0;
		}
	}
	/**
	 * This is a simple Exception class that is thrown when you attempt to incorrectly call the writeBit method.
	 * You do not need to do anything with this exception, they are just to make debugging easier
	 * @author Shane Paul
	 * 7/10/2003
	 * @
	 */
	public class InvalidBitException extends Exception{

	}
	/**
	 * This is an exception to inform you that you are trying to write to a bitwriter that has already been closed.
	 * @author Shane Paul
	 * 7/10/2003
	 * @
	 */
	public class BitWriterClosedAlreadyException extends Exception{

	}

	/**
	 * Testing...and an example of how to use this class.
	 * @param args
	 */
	public static void main(String[] args) throws Exception{
		BitWriter bw = new BitWriter("writetest.txt");
		for(int j=0;j<12;j++){
			int num=(int)(Math.random()*30);
            if (num % 2 == 0) {
            	System.out.println("0");
             	bw.writeBit(0);
          	}
          	else {
            	System.out.println("1");
             	bw.writeBit(1);
       		}
		}
		bw.close();
	}
}

package Code;

import java.io.*;
/**
 * This class is designed to read in bits from a file that is stored in the same format
 * as specified by the BitWriter
 * It is based on the iterator model, with similar names. It has different return types though.
 * @author Shane Paul
 * 7/10/2003
 * @
 */
public class BitReader {

	//The inputStream
	FileInputStream input;
	//Stores the next 3 bytes in the file, to check for EOF and deal with the last byte being potentially half full
	int current, plus1, plus2;
	//The number of bits that have been read so far in the current byte
	int count;
	//Have we reached the end of the file? If so, count must be ==0 also before we are finally done
	boolean finished;

	/**
	 * Construct a new BitReader from a file.
	 */
	public BitReader(String file) throws Exception{
		this(new File(file));
	}
	public BitReader(File file) throws Exception{
		file.createNewFile();
		if(!file.canRead())
			throw new IOException("Oh My!! This file cannot be read from.....whatever shall I do? \nfile: "+file);
		input = new FileInputStream(file);
		//Read in the first two bytes. This assumes that the list contains at least one bit and therefore two bytes...
		plus1 = input.read();
		plus2 = input.read();
	}
	/**
	 * Returns the next available bit in the file
	 * @return the next boolean in the file
	 */
	public boolean next()throws Exception{
		if(!hasNext())
			throw new NoBitsLeftToReturn();
		//Get the next bit
		if(count==0){ //We have no more bits in this particular byte
			//Get the next byte from the file
			current=plus1;
			plus1=plus2;
			plus2 = input.read(); //-1 is returned if we reach the EOF
			count=8;
			if(plus2<0){
				finished = true;
				count = 8-plus1;  //only need to read the leftover bits
			}
		}
		count--;
		//get the leftmost bit and shift to right most
        int bit = current&0x80;
        bit = bit>>7;
        //shift current to left for one bit
        current = current<<1;
		if(bit==0)
			return false;
		return true;
	}
	/**
	 * returns whether or not there are any bits left to read.
	 */
	public boolean hasNext(){
		return !(finished && count==0);
	}
	/**
	 * Thiis an informative exception that is thrown when you forget to check hasNext()
	 * @author Shane Paul
	 * 7/10/2003
	 * @
	 */
	public class NoBitsLeftToReturn extends Exception{}
	/**
	 * Testing
	 * @param args
	 */
	public static void main(String[] args) throws Exception{
		BitReader br = new BitReader("writetest.txt");
		while(br.hasNext()){
			if(br.next())
				System.out.println("1");
			else
				System.out.println("0");
		}
	}
}

下面就是重中之重的主操作类,代码中提供了相关注释,在此不做赘述

package Code;

import java.io.*;
import java.util.*;

/**
 可以为这个类添加额外的方法及数据成员.

 @author  	赵鹏
 @version	2018/7/12
 **/

public class TextZip {

	//ID, 该学号的值需要修改!
	private static final String ID = "201692185";
	private static Map<Character, String> value_table = new HashMap<Character, String>();

	/**
	 * This method generates the huffman tree for the text: "abracadabra!"
	 *
	 * @return the root of the huffman tree
	 */

	public static TreeNode abracadbraTree() {
		TreeNode n0 = new TreeNode(new CharFreq('!', 1));
		TreeNode n1 = new TreeNode(new CharFreq('c', 1));
		TreeNode n2 = new TreeNode(new CharFreq('\u0000', 2), n0, n1);
		TreeNode n3 = new TreeNode(new CharFreq('r', 2));
		TreeNode n4 = new TreeNode(new CharFreq('\u0000', 4), n3, n2);
		TreeNode n5 = new TreeNode(new CharFreq('d', 1));
		TreeNode n6 = new TreeNode(new CharFreq('b', 2));
		TreeNode n7 = new TreeNode(new CharFreq('\u0000', 3), n5, n6);
		TreeNode n8 = new TreeNode(new CharFreq('\u0000', '7'), n7, n4);
		TreeNode n9 = new TreeNode(new CharFreq('a', 5));
		TreeNode n10 = new TreeNode(new CharFreq('\u0000', 12), n9, n8);
		return n10;
	}

	/**
	 * This method decompresses a huffman compressed text file.  The compressed
	 * file must be read one bit at a time using the supplied BitReader, and
	 * then by traversing the supplied huffman tree, each sequence of compressed
	 * bits should be converted to their corresponding characters.  The
	 * decompressed characters should be written to the FileWriter
	 *
	 * @param br the BitReader which reads one bit at a time from the
	 *           compressed file
	 *           huffman the huffman tree that was used for compression, and
	 *           hence should be used for decompression
	 *           fw      a FileWriter for storing the decompressed text file
	 */
	public static void decompress(BitReader br, TreeNode huffman, FileWriter fw) throws Exception {    //解码

		// IMPLEMENT THIS METHOD
		//	List<Boolean> list  = new ArrayList<Boolean>();
		TreeNode temp = huffman;
		List<Boolean> list = new ArrayList<Boolean>();  //用来存code
		while (br.hasNext()) {
			list.add(br.next());
		}
		for (int i = 0; i < list.size(); i++) {
			if (!list.get(i)) {                         //为false则是左子树
				temp = temp.getLeft();
				if (temp.isLeaf()) {                    //是叶子结点则写入字符
					fw.write(((CharFreq) temp.getItem()).getChar());
					temp = huffman;                     //重置为根结点
				}
			} else {                                    //右子树
				temp = temp.getRight();
				if (temp.isLeaf()) {
					fw.write(((CharFreq) temp.getItem()).getChar());
					temp = huffman;
				}
			}
		}
	}

	/**
	 * This method traverses the supplied huffman tree and prints out the
	 * codes associated with each character//         打印每一个字母的编码
	 *
	 * @param t the root of the huffman tree to be traversed
	 *          code a String used to build the code for each character as
	 *          the tree is traversed recursively
	 */
	public static void traverse(TreeNode t, String code) {    //递归调用
		// IMPLEMENT THIS METHOD
		if (t.isLeaf()) {
			value_table.put(((CharFreq) t.getItem()).getChar(), code);
			System.out.println(((CharFreq) t.getItem()).getChar() + "  前缀是  " + code);
		} else {
			traverse(t.getLeft(), code + "0");
			traverse(t.getRight(), code + "1");
		}

	}

	/**
	 * This method removes the TreeNode, from an ArrayList of TreeNodes,  which
	 * contains the smallest item.  The items stored in each TreeNode must
	 * implement the Comparable interface.
	 * The ArrayList must contain at least one element.
	 *
	 * @param a an ArrayList containing TreeNode objects
	 * @return the TreeNode in the ArrayList which contains the smallest item.
	 * This TreeNode is removed from the ArrayList.
	 */
	public static TreeNode removeMin(ArrayList a) {
		int minIndex = 0;
		for (int i = 0; i < a.size(); i++) {
			TreeNode ti = (TreeNode) a.get(i);
			TreeNode tmin = (TreeNode) a.get(minIndex);
			if (((Comparable) (ti.getItem())).compareTo(tmin.getItem()) < 0)
				minIndex = i;
		}
		TreeNode n = (TreeNode) a.remove(minIndex);
		return n;
	}

	/**
	 * This method counts the frequencies of each character in the supplied
	 * FileReader, and produces an output text file which lists (on each line)
	 * each character followed by the frequency count of that character.  This
	 * method also returns an ArrayList which contains TreeNodes.  The item stored
	 * in each TreeNode in the returned ArrayList is a CharFreq object, which
	 * stores a character and its corresponding frequency
	 *
	 * @param fr the FileReader for which the character frequencies are being
	 *           counted
	 *           pw the PrintWriter which is used to produce the output text file
	 *           listing the character frequencies
	 * @return the ArrayList containing TreeNodes.  The item stored in each
	 * TreeNode is a CharFreq object.
	 */
	public static ArrayList countFrequencies(FileReader fr, PrintWriter pw) throws Exception {
		// IMPLEMENT THIS METHOD
		ArrayList<TreeNode> list = new ArrayList<TreeNode>();
		Map<Character, Integer> map = new HashMap<Character, Integer>();    //每个键值对存储相应字符和对应的出现次数
		while (true) {
			int i = fr.read();
			if (i == -1) break;
			else {
				char temp = (char) i;
				if (map.get(temp) != null) {    //Map中找到就次数加一
					int key = map.get(temp);
					key++;
					map.put(temp, key);
				} else {
					map.put(temp, 1);           //没有找到就新增
				}
			}
		}
		Set<Character> set = map.keySet();
		Iterator<Character> iterator = set.iterator();
		while (iterator.hasNext()) {        //写入freq
			char k = iterator.next();
			if (k == '\r') {
				pw.println("\\r" + " " + map.get(k));
			} else if (k == '\n') {
				pw.println("\\n" + " " + map.get(k));
			} else {
				pw.println(k + " " + map.get(k));
			}

			list.add(new TreeNode(new CharFreq(k, map.get(k))));        //加入结点
		}
		return list;

	}

	/**
	 * This method builds a huffman tree from the supplied ArrayList of TreeNodes.
	 * Initially, the items in each TreeNode in the ArrayList store a CharFreq object.
	 * As the tree is built, the smallest two items in the ArrayList are removed,
	 * merged to form a tree with a CharFreq object storing the sum of the frequencies
	 * as the root, and the two original CharFreq objects as the children.  The right
	 * child must be the second of the two elements removed from the ArrayList (where
	 * the ArrayList is scanned from left to right when the minimum element is found).
	 * When the ArrayList contains just one element, this will be the root of the
	 * completed huffman tree.
	 *
	 * @param trees the ArrayList containing the TreeNodes used in the algorithm
	 *              for generating the huffman tree
	 * @return the TreeNode referring to the root of the completed huffman tree
	 */
	public static TreeNode buildTree(ArrayList<TreeNode> trees) throws IOException {
		TreeNode parent = null;
		// IMPLEMENT THIS METHOD
		while (trees.size() > 1) {
			Collections.sort(trees, new Comparator<TreeNode>() {
				//先根据freq从小到大排序
				@Override
				public int compare(TreeNode o1, TreeNode o2) {
					// TODO Auto-generated method stub
					CharFreq c1 = (CharFreq) o1.getItem();
					CharFreq c2 = (CharFreq) o2.getItem();
					return c1.getFreq() - c2.getFreq();
				}
			});
			//左右结点分别为最小的和次小的
			TreeNode left = (TreeNode) trees.get(0);
			TreeNode right = (TreeNode) trees.get(1);
			parent = new TreeNode(new CharFreq('\u0000', ((CharFreq) (left.getItem())).getFreq()
					+ ((CharFreq) (right.getItem())).getFreq()));
			parent.setLeft(left);
			parent.setRight(right);
			removeMin(trees);
			removeMin(trees);
			trees.add(parent);
		}
		return trees.get(0);    //根节点为最大的,也就是最后一个
	}

	/**
	 * This method compresses a text file using huffman encoding.  Initially, the
	 * supplied huffman tree is traversed to generate a lookup table of codes for
	 * each character.  The text file is then read one character at a time, and
	 * each character is encoded by using the lookup table.  The encoded bits for
	 * each character are written one at a time to the specified BitWriter.
	 *
	 * @param fr the FileReader which contains the text file to be encoded
	 *           huffman the huffman tree that was used for compression, and
	 *           hence should be used for decompression
	 *           bw      the BitWriter used to write the compressed bits to file
	 */

	public static void compress(FileReader fr, TreeNode huffman, BitWriter bw) throws Exception {   //压缩

		// IMPLEMENT THIS METHOD
		traverse(huffman, "");
		while (true) {
			int i = fr.read();
			if (i == -1) break;
			else {
				String data = value_table.get((char) i);    //找到对应的前缀
				for (int j = 0; j < data.length(); j++) {
					boolean k = (data.charAt(j) == '1');
					bw.writeBit(k);                         //写入压缩文件
				}
			}
		}
	}

	/**
	 * This method reads a frequency file (such as those generated by the
	 * countFrequencies() method) and initialises an ArrayList of TreeNodes
	 * where the item of each TreeNode is a CharFreq object storing a character
	 * from the frequency file and its corresponding frequency.  This method provides
	 * the same functionality as the countFrequencies() method, but takes in a
	 * frequency file as parameter rather than a text file.
	 *
	 * @param inputFreqFile the frequency file which stores characters and their
	 *                      frequency (one character per line)
	 * @return the ArrayList containing TreeNodes.  The item stored in each
	 * TreeNode is a CharFreq object.
	 */
	public static ArrayList readFrequencies(String inputFreqFile) throws Exception {
		InputStreamReader reader = new InputStreamReader(new FileInputStream(inputFreqFile));
		BufferedReader buffer = new BufferedReader(reader);
		ArrayList<TreeNode> trees = new ArrayList<TreeNode>();
		StringBuilder builder = new StringBuilder();
		int c = 0;  //当前字符
		int c1 = 0; //前一个字符
		while (true) {
			c = buffer.read();
			if (c == -1) break;
			if (c == '\n' && c1 == '\r') {
				String data1 = builder.substring(0, builder.length() - 1);
				if (data1.charAt(0) == '\\' && data1.charAt(1) == 'n') {
					builder = new StringBuilder();//每次遇到换行就清空
					trees.add(new TreeNode(new CharFreq('\n', Integer.parseInt(data1.substring(3, data1.length())))));
				} else if (data1.charAt(0) == '\\' && data1.charAt(1) == 'r') {
					builder = new StringBuilder();
					trees.add(new TreeNode(new CharFreq('\r', Integer.parseInt(data1.substring(3, data1.length())))));
				} else {
					System.out.println(data1 + " " + data1.charAt(0) + data1.substring(2, data1.length()));
					builder = new StringBuilder();
					trees.add(new TreeNode(new CharFreq(data1.charAt(0), Integer.parseInt(data1.substring(2, data1.length())))));
				}
			} else {
				builder = builder.append((char) c);
			}
			c1 = c;
		}
		// IMPLEMENT THIS METHOD
		return trees;
	}

	/* This TextZip application should support the following command line flags:
	QUESTION 2 PART 1
	=================
		 -a : this uses a default prefix code tree and its compressed
		      file, "a.txz", and decompresses the file, storing the output
		      in the text file, "a.txt".  It should also print out the size
		      of the compressed file (in bytes), the size of the decompressed
		      file (in bytes) and the compression ratio
	QUESTION 2 PART 2
	=================
		 -f : given a text file (args[1]) and the name of an output frequency file
		      (args[2]) this should count the character frequencies in the text file
		      and store these in the frequency file (with one character and its
		      frequency per line).  It should then build the huffman tree based on
		      the character frequencies, and then print out the prefix code for each
		      character
	QUESTION 2 PART 3
	=================
		 -c : given a text file (args[1]) and the name of an output frequency file
		      (args[2]) and the name of the output compressed file (args[3]), this
		      should compress file
	QUESTION 2 PART 4
	=================
		 -d : given a compressed file (args[1]) and its corresponding frequency file
		      (args[2]) and the name of the output decompressed text file (args[3]),
		      this should decompress the file
	*/

	public static void main(String[] args) throws Exception {

		if (args[0].equals("-a")) {
			BitReader br = new BitReader("a.txz");
			FileWriter fw = new FileWriter("a.txt");

			// Get the default prefix code tree
			TreeNode tn = abracadbraTree();
			// Decompress the default file "a.txz"
			decompress(br, tn, fw);      //解压缩

			// Close the ouput file
			fw.close();
			File f1 = new File("a.txz");
			File f2 = new File("a.txt");
			System.out.println("a.txz decompressed by " + ID);
			System.out.println("Size of the compressed file: " + f1.length());
			System.out.println("Size of the original file: " + f2.length());
			System.out.println("压缩比为:" + (double) f1.length() / f2.length() * 100 + "%");
			// Output the compression ratio   输出压缩比
			// Write your own implementation here.
		} else if (args[0].equals("-f")) {  //-f file.txt file.freq
			value_table.clear();
			FileReader fr = new FileReader(args[1]);
			PrintWriter pw = new PrintWriter(new FileWriter(args[2]));

			// Calculate the frequencies
			ArrayList trees = countFrequencies(fr, pw);  //计算数据次数
			// Close the files
			fr.close();
			pw.close();

			// Build the huffman tree
			TreeNode n = buildTree(trees);
			System.out.println(args[1] + "prefix codes by " + ID);
			// Display the codes
			traverse(n, "");
		} else if (args[0].equals("-c")) { //-c file.txt file.freq file.txz
			value_table.clear();
			FileReader fr = new FileReader(args[1]);
			PrintWriter pw = new PrintWriter(new FileWriter(args[2]));
			ArrayList<TreeNode> trees = countFrequencies(fr, pw);
			fr.close();
			pw.close();
			TreeNode n = buildTree(trees);

			// IMPLEMENT NEXT
			// Finish the compress function here

			FileReader fr1 = new FileReader(args[1]);
			BitWriter bw = new BitWriter(args[3]);
			compress(fr1, n, bw);
			bw.close();
			File f1 = new File("file.txz");
			File f2 = new File("file.txt");
			System.out.println(f2.getName() + " compressed by " + ID);
			System.out.println(f1.getName() + "的大小为:" + f1.length() + "bytes");
			System.out.println(f2.getName() + "的大小为:" + f2.length() + "bytes");
			System.out.println("压缩率为" + (double) (f1.length()) / ((double) (f2.length())) * 100 + "%");
			// then output the compression ratio
			// then output the compression ratio
			// Write your own implementation here.
		} else if (args[0].equals("-d")) {
			value_table.clear();
			ArrayList a = readFrequencies(args[2]);    //现在解压   -d file.txz file.freq file.txt
			TreeNode tn = buildTree(a);
			BitReader br = new BitReader(args[1]);
			FileWriter fw = new FileWriter(args[3]);
			decompress(br, tn, fw);
			fw.close();
			File f1 = new File(args[1]);
			File f2 = new File(args[3]);
			System.out.println(f1.getName() + " decompressed by " + ID);
			System.out.println(args[1] + "的大小为:" + f1.length() + "bytes");
			System.out.println(args[3] + "的大小为:" + f2.length() + "bytes");
			System.out.println("压缩率为" + (double) (f1.length()) / ((double) (f2.length())) * 100 + "%");
			// Output the compression ratio
			// Write your own implementation here.


		}
	}
}

注意:

主函数中获取的是main传入的参数,需要在虚拟机工作的文件路径执行命令行操作

相关参数为 

java TextZip -a    :  将固定写入的a.tez文件解压成原文件

java TextZip -f  [@file.txt] [@file.freq]  :     根据传入的txt文件解析文本权重并创建输出到freq文件中

java TextZip -c [@file.txt] [@file.freq] [@file.txz]    :    根据传入的txt文件解析文本权重创建输出到freq

                                                                                            文件中并压缩文件为txz

java TextZip -d [@file.txz] [@file.freq] [@file.txt]     :    根据传入的txz和freq文件解压缩,生成txt文件


jet brain的IDEA提供了指定命令行参数的简便方法~可参见我的另一篇说明

 

猜你喜欢

转载自blog.csdn.net/zp_icenow/article/details/81042158
今日推荐