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