哈夫曼编码(Huffman)Java实现代码
生活随笔
收集整理的這篇文章主要介紹了
哈夫曼编码(Huffman)Java实现代码
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
網上找到的一個組Huffman編碼Java實現代碼,比較經典。
1、主類,壓縮和解壓
package cn.hm;import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.DataInputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; public class HZIP { /** * 壓縮文件的方法,此方法需要傳入正確的絕對路徑名 * @param inFile 需要被壓縮的文件 * @param outFile 壓縮之后的輸出文件 * @throws IOException IO異常 */ public static void compress(String inFile,String outFile) throws IOException{ String compressFile=null;//創建壓縮文件 String extension=inFile.substring(inFile.length()-4);//獲取源文件的后綴名 File file=new File(outFile); //如果文件已經存在 if(file.exists()){ System.out.println("文件已經存在"); }else{ //自動補齊后綴名 if(!outFile.endsWith(".hfm")){ compressFile=outFile+extension+".hfm"; } else{ compressFile=outFile+extension; } //創建文件輸入的緩沖流 InputStream in=new BufferedInputStream(new FileInputStream(inFile)); //創建文件輸出的緩沖流 OutputStream out=new BufferedOutputStream(new FileOutputStream(compressFile)); int ch; //創建哈弗曼壓縮的輸入流 HZIPOutputStream hzout=new HZIPOutputStream(out); while((ch=in.read())!=-1){ hzout.write(ch); } //關閉流 in.close(); hzout.close(); } } /** * 解壓文件的方法,此方法需要填入正確的絕對路徑名 * @param compressedFile 需要被解壓的文件 * @param outFile 解壓之后的輸出文件 * @throws IOException IO異常 */ public static void uncompress(String compressedFile,String outFile) throws IOException{ String extension;//文件的后綴名 extension =compressedFile.substring(compressedFile.length()-4); //得到壓縮前的文件的后綴名 String suffix=compressedFile.substring(compressedFile.length()-8,compressedFile.length()-4); //如果文件不合法則不執行解壓操作 if(!extension.equals(".hfm")){ System.out.println("文件格式錯誤或者不是有效的壓縮文件"); return; } File file=new File(outFile); //如果已經存在同名文件 if(file.exists()){ System.out.println("該文件已經存在,請重新命名解壓后的文件"); } else{ outFile+=(suffix+".uc");//輸出文件的格式統一為uc格式 //創建文件輸入的緩沖流 InputStream fin=new BufferedInputStream(new FileInputStream(compressedFile)); //創建數據讀入流 DataInputStream in=new DataInputStream(fin); //創建哈弗曼壓縮輸入流 HZIPInputStream hzin=new HZIPInputStream(in); //創建文件輸出的緩沖流 OutputStream fout=new BufferedOutputStream(new FileOutputStream(outFile)); int ch; //解壓并輸出文件 while((ch=hzin.read())!=-1){ fout.write(ch); } //關閉流 hzin.close(); fout.close(); } } public static void main(String args[]) throws IOException{ System.out.println("開始壓縮"); long start=System.currentTimeMillis(); compress("d://tmp/ori.txt","d://tmp/des"); System.out.println("壓縮結束,用時:"+(System.currentTimeMillis()-start)); System.out.println("開始解壓"); start=System.currentTimeMillis(); uncompress("d://tmp/des.txt.hfm","d://tmp/ori1"); System.out.println("解壓結束,用時:"+(System.currentTimeMillis()-start));} }2、字符頻度統計類
package cn.hm;/** * 字符統計類,獲取輸入流(通常是文件)中所含的字符數 * 8位字節認為是ASCII字符 */ import java.io.IOException; import java.io.InputStream; public class CharCounter { //字節的下標表示字節的種類,對應的值表示出現的次數 private int theCounts[]=new int[BitUtils.DIFF_BYTES];//字節的種類總共有256種 /** * 默認的無參的構造方法 */ public CharCounter(){ } /** * 封裝了基本的InputStream,讀取數據并進行字符的頻次統計 * @param input InputStream對象 * @throws IOException */ public CharCounter(InputStream input) throws IOException{ int ch;//讀到的字節 //一直讀到文件的末尾,每一種byte出現了多少次 while((ch=input.read())!=-1){ theCounts[ch]++; } } /** * 獲取該字符統計數組的某一個字符出現的次數 * @param ch 數組下標 * @return 該下標位置字符出現的次數 */ public int getCount(int ch){ return theCounts[ch&0xff]; } /** * 設置某一個字符出現的次數 * @param ch 數組下標 * @param count 字符出現次數 */ public void setCount(int ch,int count){ theCounts[ch&0xff]=count; } }3、Huffman樹編碼類
package cn.hm;import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.util.PriorityQueue; public class HuffmanTree { private CharCounter theCounts;//字符統計類對象 private HuffNode root;//根結點 private HuffNode[] theNodes=new HuffNode[BitUtils.DIFF_BYTES+1];//存儲HuffNode的數組 public static final int ERROR=-3;//錯誤 public static final int INCOMPLETE_CODE=-2;//不完全的結點編碼 public static final int END=BitUtils.DIFF_BYTES;//字節的溢出位 /** * 實例化一個字符統計類對象 */ public HuffmanTree(){ theCounts=new CharCounter(); root=null; } /** * 可以通過CharCounter對象來創建一個huffmanTree對象 * @param cc CharCounter對象 */ public HuffmanTree(CharCounter cc){ theCounts=cc; root=null; createTree();//創建HuffmanTree } /** * 得到要尋找的字符編碼所在的樹結點,如果該字符不在樹上,則返回null表示出錯,否則,通過父親鏈逆向查找,直到到達根結點 * @param ch 當前結點的下標 * @return 結點相對的字符編碼數組 */ public int[] getCode(int ch){ HuffNode current=theNodes[ch]; if(current==null) return null; String v="";//結點的編碼 HuffNode parent=current.parent; while(parent!=null){ if(parent.left==current) v="0"+v;//左結點編碼 else v="1"+v;//右結點編碼 //向下遍歷 current=current.parent; parent=current.parent; } int len=v.length(); int [] result=new int[len];//創建一個與編碼相同大小數組 for(int i=0;i<len;i++) result[i]=v.charAt(i)=='0'?0:1; return result; } /** * 獲取編碼對應的字符 * @param code 哈弗曼編碼 * @return 存儲在結點中的值(如果結點不是葉子結點,則返回符號INCOMPLETE) */ public int getChar(String code){ HuffNode leaf=root;//獲取根結點 int len=code.length(); //按照編碼向左或向右遍歷到葉子結點 for(int i=0;leaf!=null&&i<len;i++) if(code.charAt(i)=='0') leaf=leaf.left; else leaf=leaf.right; //根結點為空 if(leaf==null) return ERROR; return leaf.value; } /** * 寫入編碼表的方法 * @param out 寫入的數據流 * @throws IOException */ public void writeEncodingTable(DataOutputStream out) throws IOException{ for(int i=0;i<BitUtils.DIFF_BYTES;i++){ if(theCounts.getCount(i)>0){ out.writeByte(i);//將字節寫入(通常是文件) out.writeInt(theCounts.getCount(i));//將字節出現的次數寫入(通常是文件) } } //最后寫入0表示編碼的結束 out.writeByte(0); out.writeInt(0); } /** * 讀取編碼表的方法 * @param in 數據輸入流對象 * @throws IOException */ public void readEncodingTable(DataInputStream in) throws IOException{ for(int i=0;i<BitUtils.DIFF_BYTES;i++) theCounts.setCount(i, 0); byte ch; int num; for(;;){ ch=in.readByte();//讀到的字節 num=in.readInt();//字符出現的次數 if(num==0)//如果讀到0表示編碼表的結束 break; theCounts.setCount(ch, num); } createTree();//創建HuffmanTree } /** * 構造哈弗曼編碼樹的方法 */ public void createTree(){ //創建一個優先隊列來保存HuffNode PriorityQueue<HuffNode> pq=new PriorityQueue<HuffNode>(); for(int i=0;i<BitUtils.DIFF_BYTES;i++){ if(theCounts.getCount(i)>0){//如果某一個字節出現過 HuffNode newNode=new HuffNode(i,theCounts.getCount(i),null,null,null); theNodes[i]=newNode; pq.add(newNode);//將新結點添加到隊列中 } } theNodes[END] =new HuffNode(END,1,null,null,null); pq.add(theNodes[END]); //當剩余的結點多于一個時 while(pq.size()>1){ //每次取出當前最小的兩個結點 HuffNode n1=pq.remove();//remove方法與poll方法的唯一不同之處在于:此隊列為空時將拋出一個異常 HuffNode n2=pq.remove(); //將最小的兩個結點鏈接形成新結點 HuffNode result=new HuffNode(INCOMPLETE_CODE,n1.weight+n2.weight,n1,n2,null); n1.parent=n2.parent=result; //將新結點添加到隊列當中 pq.add(result); } root=pq.element();//根結點就是隊列中的最后一個結點 } } package cn.hm;/** * 哈弗曼結點類 */ public class HuffNode implements Comparable<HuffNode>{ public int value;//結點數據 public int weight;//權重 HuffNode left,right;//左右孩子結點 HuffNode parent;//父親結點 /** * 初始化結點的數據,權重,左右孩子結點與父親結點 * @param v 數據 * @param w 權重 * @param lchild 左孩子結點 * @param rchild 右孩子結點 * @param pt 父親結點 */ HuffNode(int v,int w,HuffNode lchild,HuffNode rchild,HuffNode pt){ value=v; weight=w; left=lchild; right=rchild; parent=pt; } /** * 比較兩個結點的權重 */ public int compareTo(HuffNode rhs) { return weight-rhs.weight; } }4、輸入輸出輔助類
package cn.hm;/** * 包含壓縮的包裝類 */ import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.DataOutputStream; import java.io.IOException; import java.io.OutputStream; public class HZIPOutputStream extends OutputStream{ private ByteArrayOutputStream byteOut=new ByteArrayOutputStream();//實例化的一個字節數組輸出流對象 private DataOutputStream dout;//數據輸出流對象 /** * 實例化一個DataOutputStream對象的構造方法 * @param out 輸出流對象 * @throws IOException */ public HZIPOutputStream(OutputStream out) throws IOException{ dout=new DataOutputStream(out); } /** * 寫入編碼頻率的方法 */ public void write(int ch) throws IOException{ byteOut.write(ch); } /** * 關閉流的方法 */ public void close() throws IOException{ byte[] theInput=byteOut.toByteArray();//將字節數組輸出流轉換數據轉換成字節數組進行輸入 ByteArrayInputStream byteIn=new ByteArrayInputStream(theInput);//將字節數組封裝到字節輸入流中 CharCounter countObj=new CharCounter(byteIn);//實例化字符統計對象并統計字節數組的字符出現的次數 byteIn.close();//關閉字節輸入流 HuffmanTree codeTree=new HuffmanTree(countObj);//通過CharCounter對象實例化一個HuffmanTree對象 codeTree.writeEncodingTable(dout);//將編碼寫入數據輸出流中 BitOutputStream bout=new BitOutputStream(dout);//創建位輸出流 //將按編碼轉換后的位寫入 int len=theInput.length; for(int i=0;i<len;i++) bout.writeBits(codeTree.getCode(theInput[i]&0xff)); bout.writeBits(codeTree.getCode(BitUtils.EOF));//文件結束的標示符 //關閉流 bout.close(); byteOut.close(); } }package cn.hm; /** * 包含解壓縮的包裝類 */ import java.io.DataInputStream; import java.io.IOException; import java.io.InputStream; public class HZIPInputStream extends InputStream{ private BitInputStream bin;//位輸入流 private HuffmanTree codeTree;//編碼的HuffmanTree對象 /** * 封裝InputStream對象,實例化HuffmanTree對象與BitInputStream對象,并讀入哈弗曼編碼 * @param in * @throws IOException */ public HZIPInputStream(InputStream in) throws IOException{ //數據輸入流 DataInputStream din=new DataInputStream(in); codeTree=new HuffmanTree(); codeTree.readEncodingTable(din); bin=new BitInputStream(in); } /** * 讀取文件的方法 */ public int read() throws IOException{ String bits="";//哈弗曼編碼的字符串 int bit;//位 int decode;//解碼后的字符 while(true){ bit=bin.readBit(); if(bit == -1) throw new IOException("Unexpected EOF");//意外的資源結束 bits+=bit; decode=codeTree.getChar(bits);//獲取編碼對應的字符 if(decode==HuffmanTree.INCOMPLETE_CODE)//向下搜索到葉子結點 continue; else if(decode==HuffmanTree.ERROR)//編碼出錯 throw new IOException("Unexpected error"); else if(decode==HuffmanTree.END)//編碼溢出 return -1; else return decode; } } /** * 關閉輸入流 */ public void close() throws IOException{ bin.close(); } }
package cn.hm;/** * InputStream的包裝類,提供按位輸入 */ import java.io.IOException; import java.io.InputStream; public class BitInputStream { private InputStream in;//基本輸入流 private int buffer;//byte緩沖區 private int bufferPos;//表示緩沖區中有多少未被使用的空間 /** * 封裝InputStream的構造方法,初始化byte緩沖區的大小 * @param is InputStream對象 */ public BitInputStream(InputStream is){ in=is; bufferPos=BitUtils.BITS_PER_BYTES;//初始化緩沖區的剩余空間 } /** * 讀取一位的方法,每8次對其進行調用就會從基本輸入流中讀出一個byte * @return 1位數據,1或者0 * @throws IOException */ public int readBit() throws IOException{ //如果緩沖區還未被使用 if(bufferPos==BitUtils.BITS_PER_BYTES){ //輸入流讀取一位 buffer=in.read(); //讀到文件的末尾了 if(buffer==-1) return -1; //清空緩沖區 bufferPos=0; } //擴張緩沖區 return getBit(buffer,bufferPos++); } /** * 關閉輸入流 * @throws IOException */ public void close() throws IOException{ in.close(); } /** * 獲取一個byte中每一位的方法 * @param pack * @param pos * @return */ private static int getBit(int pack,int pos){ //將一個字節進行8次按位與運算,得到這個字節的8位 return (pack&(1<<pos))!=0?1:0; } }
package cn.hm;/** * OutputStream的包裝類,提供按位輸出的方法 */ import java.io.IOException; import java.io.OutputStream; public class BitOutputStream { private OutputStream out; //基本輸出流 private int buffer;//輸出的緩沖區 private int bufferPos;//緩沖區中剩余的位數 /** * 封裝OutputStream的構造方法,初始化緩沖區大小 * @param os */ public BitOutputStream(OutputStream os){ bufferPos=0; buffer=0; out=os; } /** * 寫入一串的位 * @param val 包含有位數據的數組 * @throws IOException */ public void writeBits(int []val) throws IOException{ int len=val.length; for(int i=0;i<len;i++){ writeBit(val[i]); } } /** * 寫入位的方法(0或1),每8次對其進行調用就從基本流中寫入一個byte * @param val 當前寫入的位數據 * @throws IOException */ public void writeBit(int val) throws IOException{ buffer=setBit(buffer,bufferPos++,val);//將緩沖數據轉換成位數據 //每讀到一個byte就刷新一次 if(bufferPos==BitUtils.BITS_PER_BYTES)//緩沖區已滿則刷新緩沖區 flush(); } /** * 刷新此緩沖的輸出流 * @throws IOException */ public void flush() throws IOException{ if(bufferPos==0)//如果緩沖中沒有數據則不執行 return; //將緩沖區中的數據寫入 out.write(buffer); //重置緩沖區 bufferPos=0; buffer=0; } /** * 關閉流的方法 * @throws IOException */ public void close() throws IOException{ flush(); out.close(); } /** * 進行位數據轉換的方法 * @param pack * @param pos * @param val 當前位 * @return */ private int setBit(int pack,int pos,int val){ if(val==1) //按位或運算 pack|=(val<<pos); return pack; } }
package cn.hm;public interface BitUtils {public static final int BITS_PER_BYTES=8;//位與byte之間的轉換單位 public static final int DIFF_BYTES=256;//0x100 public static final int EOF=256;//EndOfFile 資料源無更多的資料可讀取 }
總結
以上是生活随笔為你收集整理的哈夫曼编码(Huffman)Java实现代码的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Java字节码进制转换
- 下一篇: 哈夫曼编码(Huffman)Java实现