哈夫曼树简单java实现文本压缩与解压

哈夫曼树实现文本的压缩和解压

哈夫曼树这里就不在做相关介绍,网上也有很多哈夫曼树的介绍,这里主要介绍利用哈夫曼树进行文件的压缩与解压

首先是哈夫曼树的建立:ASCII 码一共有256个(常用的有128个)建立的时候可以选择一起将全部ASCII一起建立也可以选择按文档中出现的字符建立。虽然如果选择将所有字符一起建立的话可能会多很多,但由于在合并的之后所有0的点都会合并在一起所以对文件编码长度几乎没有影响。

下面是节点的结构与初始化

    class HTNODE{
        int weight;//权值
        int lchid;
        int rlight;
        int parent;
        int index;//下标
    }
    void InitHT(){
        for (int i=0;i<2*N-1;i++){
            htnode[i] = new HTNODE();
            htnode[i].lchid = -1;
            htnode[i].rlight = -1;
            htnode[i].parent = -1;
            htnode[i].weight = 0;
            htnode[i].index = i;
        }
    }

这里增加了一个下标,方便解压时的转化

前256个节点即为字符ASCII对应的下标,这样在压缩和解压时即可通过下标直接由节点转化为相应的字符

首先是从文件中按字符读取文本内容,每读取到一个字符其ASCII数值的结点下标权值相应加1

void readfile(){
        File file = new File("originalfile.txt");
        Reader reader;
        try {
            // 一次读一个字符
            reader = new InputStreamReader(new FileInputStream(file));
            int tempchar;
            while ((tempchar = reader.read()) != -1) {
                // 对于windows下,\r\n这两个字符在一起时,表示一个换行。
                // 但如果这两个字符分开显示时,会换两次行。
                // 因此,屏蔽掉\r,或者屏蔽\n。否则,将会多出很多空行。
                if (((char) tempchar) != '\r') {
                    oldfile += (char) tempchar;
                    htnode[tempchar].weight++;
                }
            }
            reader.close();
        }
        catch (IOException e) {
            System.out.println("打开文件失败\n");
        }
    }

接下来是寻找节点中权值最小的两个结点并返回下标

    int[] findmin(int i){
        int[] min = new int[2];
        min[0] = Integer.MAX_VALUE;
        min[1] = Integer.MAX_VALUE;
        int[] index = {0,0};
        for (int j=0; jhtnode[j].weight)&&(htnode[j].parent == -1)){
                min[1] = min[0];
                min[0] = htnode[j].weight;
                index[1] = index[0];
                index[0] = j;
            }
            else if((min[1]>htnode[j].weight)&&(htnode[j].parent == -1)){
                min[1] = htnode[j].weight;
		index[1] = j;
            }
        }
        return index;
    }

接下来是对结点的合并,前N个节点为基础结点,所以合并从下标为N的结点开始,寻找所有结点中权值最小的两个结点进行合并

    void CreatHT(){
        int[] min;
        for(int i=N;i<2*N-1;i++){
            min = findmin(i);
            htnode[min[0]].parent = i;
            htnode[min[1]].parent = i;
            htnode[i].lchid = min[0];
            htnode[i].rlight = min[1];
            htnode[i].weight = htnode[min[0]].weight + htnode[min[1]].weight;
        }
    }

之后是哈夫曼编码的建立,要求的哈夫曼编码从前N个基础的字符根节点,所以可以从前N个叶子结点开始判断其是父节点的左孩子还是右孩子,左孩子为0右孩子为1,直到根节点这样就得到了ASCII表中全部字符的Huffman编码

    void SetHuffmanEncoding(){
        int p;
        for (int i=0;i=0){
                char r = ((htnode[p].lchid == d)? '0' : '1');
                code = r + code;
                d = p;
            }
            hfcode[i].s = code;
        }
    }

现在到了真正压缩的环节,hufffman编码我们已经得到了,接下来要做的就是将文件内容转化为Huffman编码,每个字符的ASCII码即为每个结点对应的下标,所以将字符转化为其相应的Huffman编码是很简单的,但直接转化显然是不可以的,这将原本一个字符变成了一个多位的编码,这样显然达不到压缩的效果,反而会变的更大,所以我们要将字符串的编码转化成证书型,并将每八位写道一个字节里。这里有一个write方法,可以按字节写入到文件中,如果是int型将写低八位。

在压缩过程中也要输出哈夫曼编码,在解压过程中根据这个文件重建huffman码表,这里偷了个懒,由于上面是每八尾写入一个字节的,所以最后可能少于八位便加0补全,这样结尾就可能多出原本不存在的字符,于是在huffman表中我在最后又写入了编码一共有多少位这样就可以避免了。

    void compress() throws FileNotFoundException, IOException{
        FileOutputStream fos = new FileOutputStream("compressedfile.txt");
        for (int i=0;i

最后就是解压了,首先要通过huffman表重建Huffman树,然后读入压缩文件,将字节转化为字符的Huffman码,再由此就能得到其对应的字符,接下来是完整代码仅供参考

/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package huffmantree;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;

/**
 *
 * @author 吴昊
 */
public class Huffmantree {

    /**
     * @param args the command line arguments
     */
    static final int N = 256;
    HTNODE[] htnode= new HTNODE[2*N-1];
    huffmancode[] hfcode = new huffmancode[256];
    String oldfile="";
    String newfile="";
    int[] b;
    char[] news;
    public static void main(String[] args) throws IOException {
        // TODO code application logic here
        Huffmantree hf = new Huffmantree();
        hf.InitHT();
        hf.readfile();
        hf.CreatHT();
        hf.SetHuffmanEncoding();
        hf.compress();
        hf.uncompress();
    }
    void readfile(){
        File file = new File("originalfile.txt");
        Reader reader;
        try {
            // 一次读一个字符
            reader = new InputStreamReader(new FileInputStream(file));
            int tempchar;
            while ((tempchar = reader.read()) != -1) {
                // 对于windows下,\r\n这两个字符在一起时,表示一个换行。
                // 但如果这两个字符分开显示时,会换两次行。
                // 因此,屏蔽掉\r,或者屏蔽\n。否则,将会多出很多空行。
                if (((char) tempchar) != '\r') {
                    oldfile += (char) tempchar;
                    htnode[tempchar].weight++;
                }
            }
            reader.close();
        }
        catch (IOException e) {
            System.out.println("打开文件失败\n");
        }
    }
    class HTNODE{
        int weight;
        int lchid;
        int rlight;
        int parent;
        int index;
    }
    void InitHT(){
        for (int i=0;i<2*N-1;i++){
            htnode[i] = new HTNODE();
            htnode[i].lchid = -1;
            htnode[i].rlight = -1;
            htnode[i].parent = -1;
            htnode[i].weight = 0;
            htnode[i].index = i;
        }
    }
    int[] findmin(int i){
        int[] min = new int[2];
        min[0] = Integer.MAX_VALUE;
        min[1] = Integer.MAX_VALUE;
        int[] index = {0,0};
        for (int j=0; jhtnode[j].weight)&&(htnode[j].parent == -1)){
                min[1] = min[0];
                min[0] = htnode[j].weight;
                index[1] = index[0];
                index[0] = j;
            }
            else if((min[1]>htnode[j].weight)&&(htnode[j].parent == -1)){
                min[1] = htnode[j].weight;
		index[1] = j;
            }
        }
        return index;
    }
    void CreatHT(){
        int[] min;
        for(int i=N;i<2*N-1;i++){
            min = findmin(i);
            htnode[min[0]].parent = i;
            htnode[min[1]].parent = i;
            htnode[i].lchid = min[0];
            htnode[i].rlight = min[1];
            htnode[i].weight = htnode[min[0]].weight + htnode[min[1]].weight;
        }
    }
    void output(){
        for (int i=0;i<2*N-1;i++){
            if (htnode[i].weight!=0)
                System.out.print(i+"/"+htnode[i].weight+"/"+htnode[i].lchid+"/"+htnode[i].rlight+"   ");
        }
        System.out.println("");
    }
    class huffmancode{
        char c;
        String s;
    }
    void SetHuffmanEncoding(){
        int p;
        for (int i=0;i=0){
                char r = ((htnode[p].lchid == d)? '0' : '1');
                code = r + code;
                d = p;
            }
            hfcode[i].s = code;
        }
    }
    void compress() throws FileNotFoundException, IOException{
        FileOutputStream fos = new FileOutputStream("compressedfile.txt");
        for (int i=0;i>1);
            a=(byte)(a<<1);
            if(a==c){
                res="0"+res;
            }
            else{
                res="1"+res;
            }
            a=(byte)(a>>1);
        }
        return res;
    }
}

 

你可能感兴趣的:(哈夫曼树简单java实现文本压缩与解压)