package lesson4;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Collections;
import java.util.PriorityQueue;
public class HuffmanCode {
private String path;//文件的输入路径
private byte byteCount[] = new byte[256];//每个字节的计数
hfmNode root=null;//定义的根节点
//private int fileLen;//input文件长度
private Code SaveCode[]=new Code[256];
/**
* 写一个构造器来传入path
* @param path 文件路径
*/
public HuffmanCode(String path){
this.path=path;
for (int i=0;i<256;i++){//频率清零 初始化
byteCount[i]=0;
Code t = new Code();
t.n=0;
t.node="";
SaveCode[i]=t;
}
}
/**
* 读入文件
*/
public void readFile(){
try {
// 创建文件输入流
java.io.FileInputStream fis = new java.io.FileInputStream(path);
//读入所有的文件字节
while(fis.available()>0){
int i=fis.read();
byteCount[i]++;
}
System.out.println("读入文件");
//构建哈弗曼树
createTree();
//生成编码
getMB(root,"");
fis.close();//关闭文件流
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 输出压缩文件
*/
public void writeFile(){
try {
// 创建文件输入流
java.io.FileInputStream fis = new java.io.FileInputStream(path);
// 创建文件输出流
java.io.FileOutputStream fos = new java.io.FileOutputStream(path+".stzip");
//写出每一个编码的长度
for (int i=0;i<256;i++){
fos.write(SaveCode[i].n);
}
//////////////////////////////////////////写入每一个字节锁对应的 String编码
int count=0;//记录中转的字符个数
int i=0;//第i个字节
String writes ="";
String tranString="";//中转字符串
String waiteString;//保存所转化的所有字符串
while((i<256)||(count>=8)){
//如果缓冲区的等待写入字符大于8
if (count>=8){
waiteString="";//清空要转化的的码
for (int t=0;t<8;t++){
waiteString=waiteString+writes.charAt(t);
}
//将writes前八位删掉
if (writes.length()>8){
tranString="";
for (int t=8;t<writes.length();t++){
tranString=tranString+writes.charAt(t);
}
writes="";
writes=tranString;
}else{
writes="";
}
count=count-8;//写出一个8位的字节
int intw=changeString(waiteString);//得到String转化为int
//写入文件
fos.write(intw);
}else{
//得到地i个字节的编码信息,等待写入
count=count+SaveCode[i].n;
writes=writes+SaveCode[i].node;
i++;
}
}
///////////////////////////////////////////////////////////////////
//把所有编码没有足够8的整数倍的String补0使得足够8的整数倍,再写入
if (count>0){
waiteString="";//清空要转化的的码
for (int t=0;t<8;t++){
if (t<writes.length()){
waiteString=waiteString+writes.charAt(t);
}else{
waiteString=waiteString+'0';
}
}
fos.write(changeString(waiteString));//写入
System.out.println("写入了->"+waiteString);
}
//////////////////////////////////
//再次读入文件的信息,对应每一个字节写入编码
count=0;
writes ="";
tranString="";
int idata=fis.read();
//文件没有读完的时候
while ((fis.available()>0)||(count>=8)){
//如果缓冲区的等待写入字符大于8
if (count>=8){
waiteString="";//清空要转化的的码
for (int t=0;t<8;t++){
waiteString=waiteString+writes.charAt(t);
}
//将writes前八位删掉
if (writes.length()>8){
tranString="";
for (int t=8;t<writes.length();t++){
tranString=tranString+writes.charAt(t);
}
writes="";
writes=tranString;
}else{
writes="";
}
count=count-8;//写出一个8位的字节
int intw=changeString(waiteString);
fos.write(intw);//写到文件中区
}else{
//读入idata字节,对应编码写出信息
count=count+SaveCode[idata].n;
writes=writes+SaveCode[idata].node;
idata=fis.read();
}
}
count=count+SaveCode[idata].n;
writes=writes+SaveCode[idata].node;
//把count剩下的写入
int endsint=0;
if (count>0){
waiteString="";//清空要转化的的码
for (int t=0;t<8;t++){
if (t<writes.length()){
waiteString=waiteString+writes.charAt(t);
}else{
waiteString=waiteString+'0';
endsint++;
}
}
fos.write(changeString(waiteString));//写入所补的0
System.out.println("写入了->"+waiteString+"int:"+endsint);
}
//写一个n记录所补0的个数
fos.write(endsint);
System.out.println("压缩完毕!");
fis.close();//关闭文件
fos.close();
} catch (Exception ef) {
ef.printStackTrace();
}
}
/**
* 将一个八位的字符串转成一个整数
* @param s
* @return
*/
public int changeString(String s){
return ((int)s.charAt(0)-48)*128+((int)s.charAt(1)-48)*64+((int)s.charAt(2)-48)*32
+((int)s.charAt(3)-48)*16+((int)s.charAt(4)-48)*8+((int)s.charAt(5)-48)*4
+((int)s.charAt(6)-48)*2+((int)s.charAt(7)-48);
}
/**
* 使用优先队列构建哈弗曼树
*/
public void createTree(){
//优先队列
PriorityQueue<hfmNode> nodeQueue = new PriorityQueue<hfmNode>();
//把所有的节点都加入到 队列里面去
for (int i=0;i<256;i++){
if(byteCount[i]!=0){
hfmNode node = new hfmNode(i,byteCount[i]);
nodeQueue.add(node);//加入节点
}
}
//构建哈弗曼树
while(nodeQueue.size()>1)
{
hfmNode min1 = nodeQueue.poll();//获取队列头
hfmNode min2 = nodeQueue.poll();
hfmNode result = new hfmNode(0,min1.times+min2.times);
result.lChild=min1;
result.rChild=min2;
nodeQueue.add(result);//加入合并节点
}
root=nodeQueue.peek(); //得到根节点
}
/**
* 获得叶子节点的哈弗曼编码
* @param root 根节点
* @param s
*/
public void getMB(hfmNode root,String s){
if ((root.lChild==null)&&(root.rChild==null)){
Code hc=new Code();
hc.node=s;
hc.n=s.length();
System.out.println("节点"+root.data+"编码"+hc.node);
SaveCode[root.data]=hc;//保存字节 root.data 的编码 HC
}
if (root.lChild!=null){//左0 右 1
getMB(root.lChild,s+'0');
}
if (root.rChild!=null){
getMB(root.rChild,s+'1');
}
}
}