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'); } } }