哈夫曼树这里就不在做相关介绍,网上也有很多哈夫曼树的介绍,这里主要介绍利用哈夫曼树进行文件的压缩与解压
首先是哈夫曼树的建立: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;
}
}