(Java)哈夫曼編碼譯碼器-壓縮/解壓縮編碼

哈夫曼編碼譯碼器

僅供學習和參考,這份代碼的算法和文件操作都參考了許多文章。

僅供學習和參考,這份代碼的算法和文件操作都參考了許多文章。

僅供學習和參考,這份代碼的算法和文件操作都參考了許多文章。

1. 選擇需要進行編碼的文件

2.建立哈夫曼樹

3.建立密碼本並對文件編碼

4.選擇需要進行解碼的文件並解碼

5.按位壓縮方式對文件進行壓縮

6.解壓壓縮文件爲編碼文件

一共三個類,Huffman類也是程序的入口

下面的代碼中註釋將對代碼有詳細的講解

public class Node<T> {//第一個類,可以理解爲一個鍵一個值,很簡單
    int key;
    T charData;

    Node leftChild;
    Node rightChild;
}

public class Tree implements Comparable<Tree> {
    private Node root;

    public Node getRoot() {
        return root;
    }

    public void setRoot(Node root) {
        this.root = root;
    }
    /**
     *中序遍歷
     */
    public void inDisplay(Node node){//遞歸遍歷,左根右
        if (node!=null){
            inDisplay(node.leftChild);
            System.out.println(node.key+":"+node.charData);
            inDisplay(node.rightChild);
        }
    }

    @Override
    public int compareTo(Tree o) {
        return 0;
    }
}

到這爲止都沒什麼好說的,沒涉及到什麼算法,也沒有複雜的操作,接下來是代碼的核心(帶註釋)

package Misson6_bing;
import 實驗3.KeyInput;

import java.awt.event.KeyEvent;
import java.io.*;

import java.util.HashMap;
import java.util.Map;
import java.util.PriorityQueue;

public class Huffman {
    private Map<Character,Integer> map=new HashMap<>();//存元素的哈希表
    private Map<Character,String> ce=new HashMap<>();//密碼本哈希表
    private PriorityQueue<Tree> trees=new PriorityQueue<>();//無界優先級隊列
    private String source;

    public void init(String source){
        this.source=source;
        char[] chars= source.toCharArray();//String轉char
        for (char c :chars){//迭代
            if (!map.containsKey(c)){//若無當前元素 放入map
                map.put(c,1);
            }else {
                map.put(c,map.get(c)+1);//有 則計數+1
            }
        }
        afterInit();
    }

    private void afterInit() {
        map.forEach((c,count)->{//迭代送入樹中->按優先級排序
            Node<Character> node=new Node<>();
            node.key=count;
            node.charData=c;
            Tree tree=new Tree();
            tree.setRoot(node);
            trees.add(tree);
        });
    }

    public void build(){
        while (this.trees.size()>1){//取兩個加一個,隊列中只剩下一個元素時結束
            Tree left=this.trees.poll();//隊首獲取元素,同時獲取的這個元素將從原隊列刪除
            Tree right=this.trees.poll();//隊首獲取元素,同時獲取的這個元素將從原隊列刪除
                                           //每次從隊首獲取的兩個元素是最小的兩個,第二次開始獲取的元素有一個是原本隊列中的,
                                         // 另一個是新add進來的上一次的根節點,值是上一次兩個子樹的值的和
            Node node=new Node();
            node.key=left.getRoot().key+right.getRoot().key;//根結點的值爲左右子樹之和
            node.leftChild=left.getRoot();
            node.rightChild=right.getRoot();
            left.setRoot(node);
            this.trees.add(left);
        }
    }
    public void encoding(){
        Tree tree=this.trees.peek();//查看不移除
        this.encoding(tree.getRoot(),"");
        ce.forEach((k,v)->{
            if(v.length()<8){
                String s1=String.format("%08d",Integer.valueOf(v) );//valueOf將int/String轉化爲Integer包裝類型
                ce.put(k,s1);
            }
        });

    }
    public String encodingResult(){
        StringBuilder sb=new StringBuilder();
        char[] chars= source.toCharArray();
        for (char c :chars){
            sb.append(ce.get(c));
        }
        return sb.toString();
    }
    private void encoding(Node<Character> node,String encoding){
        if (node!=null){
            if (node.leftChild==null && node.rightChild==null){
                ce.put(node.charData,encoding);
            }
            encoding(node.leftChild,encoding+"0");
            encoding(node.rightChild,encoding+"1");
        }
    }
    public void displayTree(){
        Tree tree=this.trees.peek();
        tree.inDisplay(tree.getRoot());
    }
    public void displayEncodeing(){
        ce.forEach((k,v)->{
            System.out.println(k+":"+v);
        });
    }
    public String decoding(String encodeStr,Map<Character,String> encodeMap) throws IOException {//解碼
        StringBuilder decodeStr = new StringBuilder();
        while(encodeStr.length()>1){//主循環,在解碼未結束時一直存在
            for(Map.Entry<Character,String> e: encodeMap.entrySet()){//遍歷Map中所有鍵和值;Map.Entry是Map的一個內部接口,entrySet返回值就是這個map中各個鍵值對映射關係的集合
                String charEncodeStr = e.getValue();//獲得元素的值
                if(encodeStr.startsWith(charEncodeStr)){//檢測是否以指定值開始,
                    decodeStr.append(e.getKey());//解碼的表中加入鍵
                    encodeStr = encodeStr.substring(charEncodeStr.length());//讓密碼字串從結束位置開始
                    break;
                }
            }
        }
        return decodeStr.toString();
    }
    /*
    * 創建/讀取文件
    * */
    public static boolean creatFile(String filePath) throws IOException {
        boolean flag=false;
        File newF=new File(filePath);
        if(!newF.exists()){
            try {
                newF.createNewFile();
            }
            catch (IOException e){
                e.printStackTrace();
            }
            flag=true;
        }
        return flag;
    }
    /*
    從文件中讀取字符串 讀取String類型文件
    * */
    public static String readTextFile_FR(String filePath) throws IOException {
        String thisLine=null;
        String result="";
        creatFile(filePath);
        File file=new File(filePath);
        if(file.exists()&&file.isFile()){
            try {
                BufferedReader br=new BufferedReader(new FileReader(filePath));
                while((thisLine=br.readLine())!=null){
                    result+=thisLine+"\n";
                }
                br.close();
            }
            catch (Exception e){
                e.printStackTrace();
            }
        }
        return result;
    }
    /*
    字節類型寫入文件
    * */
    public static boolean write_byte_file(String content,String filePath,boolean append){
        boolean flag=false;
        char[] chars=content.toCharArray();
        byte[] bytes = new byte[chars.length];
        int i=0;
        for(char c:chars){
            if(c=='\n'){
                break;
            }
            int t=Integer.parseInt(String.valueOf(c));

           bytes[i]=(byte)t;
           i++;
       }
        File thisFile=new File(filePath);
        try {
            if(!thisFile.exists()){
                thisFile.createNewFile();
            }
            FileOutputStream fos = new FileOutputStream(filePath);
            //System.out.println(filePath+bytes);
            fos.write(bytes);
            fos.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        flag=true;

        return flag;
    }
    /*
    按字節讀取文件內容
    * */
    public static byte[] read_byte_file(String filePath) throws IOException {

        creatFile(filePath);
        File file=new File(filePath);
        try {
            FileInputStream in =new FileInputStream(new File(filePath));
            //當文件沒有結束時,每次讀取一個字節顯示
            byte[] data=new byte[in.available()];
            in.read(data);
            in.close();
            return data;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }

    }
    /*
    壓縮String文件內容爲byte[]類型
        * */
    public static boolean compressFile_CF(String filePath_r,String filePath_w,boolean append) throws IOException {
        boolean flag=false;
        creatFile(filePath_w);
        String content=readTextFile_FR(filePath_r);//從密碼本中讀密碼
        StringBuilder content_cod=new StringBuilder();
        int j=0;
        for(int i=0;i<content.length();i+=8){
            if((8*(j+1)-1)>content.length()){//長度超過字符串長度則彈出 不然就溢出
                break;
            }
            int keys=Integer.parseInt(content.substring(8*j,(8*(j+1)-1)),2);//每8位取一次數,並二進制轉十進制
             content_cod.append(keys);//每取一個數就加進去
            // System.out.println(content_cod);
            j++;
        }
        System.out.println(content_cod);
        write_byte_file(content_cod.toString(),filePath_w,false);
        flag=true;
        System.out.println("壓縮比 "+content.length()+":"+content_cod.length());
        return flag;
    }
    /*
    * 解壓文件
    * */
    public static boolean decompressFile_DF(String filePath_com) throws IOException {
            boolean flag=false;

            byte[] bytes=read_byte_file(filePath_com);
            StringBuilder content_decom=new StringBuilder();//解碼字符串
            StringBuilder content_com=new StringBuilder();//壓縮之後的碼
          for(byte b:bytes){//迭代byte強轉int轉化爲StringBuilder
              int t=(int)b;
              content_com.append(String.valueOf(t));
          }
            char[] chars=content_com.toString().toCharArray();//將壓縮碼依次拿出來
            for(char c:chars){//迭代依次轉化壓縮碼
                if(c=='\n'){
                    break;
                }
                int temp=Integer.parseInt(String.valueOf(c));//單個字符依次轉化爲字符串再進行轉化爲二進制操作

               String s1=String.format("%08d",Integer.valueOf(Integer.toBinaryString(temp)).intValue());
               //System.out.println(s1);
               content_decom.append(s1);
            }
            writeTextFile_FW(content_decom.toString(),filePath_com,false);
            flag=true;
            return flag;

    }
    /*
    * 向文件中寫入String類型數據
    * */
    public static boolean writeTextFile_FW(String content,String filePath,boolean append){
        boolean flag=false;
        File thisFile=new File(filePath);
        try {
            if(!thisFile.exists()){
                thisFile.createNewFile();
            }
            BufferedWriter bw=new BufferedWriter(new FileWriter(filePath,append));
            bw.write(content);
            bw.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        flag=true;
        return flag;
    }
    public static void main(String[] args) throws IOException {
        Huffman huffman=new Huffman();
        int fun=-1;
        while(fun!=0){
            System.out.println("***************************************");
            System.out.println("*   1.選擇/創建需要進行編碼的文件        *");
            System.out.println("*   2.建立哈夫曼樹                      *");
            System.out.println("*   3.建立密碼本並對文件編碼             *");
            System.out.println("*   4.選擇需要解碼的文件並解碼           *");
            System.out.println("*   5.按位壓縮方式對文件進行壓縮          *");
            System.out.println("*   6.解壓壓縮文件                      *");
            System.out.println("*************************************\n");
            fun= KeyInput.readInt();
            String compress_file="D:\\\\數據結構\\\\文件存放處\\\\The compress_code.cod";
            switch (fun){
                default:
                    System.out.println("No such function found,try again!");
                    System.in.read();
                case 1:
                   System.out.println("1.D:\\\\數據結構\\\\文件存放處\\\\Test1.txt");
                   System.in.read();
                   creatFile("D:\\\\數據結構\\\\文件存放處\\\\Test1.txt");
                   System.out.println("選擇功能\n1.向文件中寫入內容\n2.直接讀取");
                   int fun1=KeyInput.readInt();
                   if(fun1==1){
                       boolean append=true;
                       System.out.println("1.選擇追加\n2.選擇覆蓋");
                       int ap=KeyInput.readInt();
                       if(ap==2){
                           append=false;
                       }
                       System.out.println("請輸入你要寫入文件的字符串");
                       String s1=KeyInput.readString();
                       writeTextFile_FW(s1,"D:\\\\數據結構\\\\文件存放處\\\\Test1.txt",append);
                   }
                   else{
                       String s=readTextFile_FR("D:\\\\數據結構\\\\文件存放處\\\\Test1.txt");
                       System.out.println(s);
                   }
                   break;
                case 2:
                    String source=readTextFile_FR("D:\\\\數據結構\\\\文件存放處\\\\Test1.txt");
                    System.out.println("文件內容爲: "+source);
                    huffman.init(source);
                    huffman.build();
                    huffman.displayTree();
                    break;
                case 3:
                    huffman.encoding();
                    huffman.displayEncodeing();
                    System.out.println( huffman.encodingResult());
                    try{
                        writeTextFile_FW(huffman.encodingResult(),"D:\\\\數據結構\\\\文件存放處\\\\The code.cod",false);
                    }
                    catch (Exception e){
                        e.printStackTrace();
                    }
                    break;
                case 4:
                    String s1=null;
                    s1=readTextFile_FR("D:\\\\數據結構\\\\文件存放處\\\\The code.cod");
                    System.out.println(s1);
                    String decode="";
                    decode=huffman.decoding(s1,huffman.ce);
                    System.out.println(decode);
                    break;
                case 5:
                    System.out.println("開始壓縮...");
                    long start=System.currentTimeMillis();

                    compressFile_CF("D:\\\\數據結構\\\\文件存放處\\\\The code.cod",compress_file,false);
                    System.out.println("壓縮結束...用時:"+(System.currentTimeMillis()-start));
                    break;
                case 6:
                    System.out.println("開始解壓...");
                    long start1=System.currentTimeMillis();
                    //String decompress_file="D:\\\\數據結構\\\\文件存放處\\\\decompress_code.cod";
                     decompressFile_DF(compress_file);
                   //  writeTextFile_FW("D:\\\\數據結構\\\\文件存放處\\\\The code.cod",false);
                    System.out.println("解壓結束...用時:"+(System.currentTimeMillis()-start1));
                    break;
                case 0:
                    break;
            }

        }
    }
}



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章