?? 這幾天完成了哈夫曼原理壓縮文件的實(shí)現(xiàn).. 雖然這個(gè)實(shí)現(xiàn)壓縮的速度相當(dāng)讓人蛋疼.. 不過(guò)這也算是加深了對(duì)壓縮原理的的理解吧.? 話說(shuō). 我還用系統(tǒng)給的類寫了個(gè)Zip格式的壓縮.. 比較之下才發(fā)現(xiàn)自己寫的那些代碼實(shí)在是不及他人的皮毛啊. 同樣是一個(gè)類. 我的效率比起系統(tǒng)的來(lái)說(shuō)......? 這根本就是沒(méi)法比啊.? 前路漫漫. 自己要學(xué)的,要改的還有很多啊..? 先談?wù)勛约旱倪@個(gè)上不了眼壓縮.. 首先是統(tǒng)計(jì)各個(gè)字節(jié)出現(xiàn)的次序
?
// 創(chuàng)建映射集,每個(gè)字節(jié)對(duì)應(yīng)其出現(xiàn)的次數(shù). HashMap<Byte, Integer> map = new HashMap<Byte, Integer>(); try {// 文件地址正確的時(shí)候創(chuàng)建文件輸入流 FileInputStream fis = new FileInputStream(path); // 封裝成緩沖流 BufferedInputStream bis = new BufferedInputStream(fis); int len = bis.available(); // 每次讀取一個(gè)字節(jié) byte data; file = new byte[len]; int i = 0; while (len > 0) { data = (byte) bis.read(); // System.out.println(data); file[i] = data; // 如果字節(jié)在映射中不存在,則放入1 if (map.get(data) == null) { map.put(data, 1); } else {// 如果字節(jié)在映射中已經(jīng)存在,則value值在原來(lái)基礎(chǔ)上加1 map.put(data, map.get(data) + 1); } i++; len = bis.available(); } fis.close(); } catch (Exception ef) { ef.printStackTrace(); }
?然后再根據(jù)各字節(jié)出現(xiàn)過(guò)的次數(shù)大小(即各個(gè)字節(jié)出現(xiàn)的頻率)來(lái)構(gòu)造哈夫曼樹(shù),并通過(guò)這棵哈夫曼樹(shù)來(lái)為每個(gè)字節(jié)編碼,于是每個(gè)字節(jié)都有一個(gè)唯一的哈夫曼編碼與之對(duì)應(yīng).然后再通過(guò)文件中各個(gè)字節(jié)的順序來(lái)得到整個(gè)文件的所有字節(jié)的哈夫曼編碼,再將這些編碼分割成8位8位的.. 然后就能將這些字符串變成字符串寫到文件中去了.
?
// 創(chuàng)建文件輸出流 FileOutputStream fos = new FileOutputStream(des); // 包裝成基本類型數(shù)據(jù)流將字節(jié)長(zhǎng)度寫入文件 DataOutputStream dos = new DataOutputStream(fos); // String轉(zhuǎn)化成的字節(jié)數(shù)組的長(zhǎng)度 dos.writeInt(str.length() / 8 + 1); byte[] by; // 字符串的長(zhǎng)度 int slen = str.length(); if (slen % 8 == 0) {// 如果字符串長(zhǎng)度正好是8的整數(shù)倍,即說(shuō)明最后沒(méi)有補(bǔ)0,byte數(shù)組的最后一個(gè)數(shù)放0,表示沒(méi)有補(bǔ)0 dos.writeInt(1);// 字符串大小正好是8的整數(shù)倍 by = new byte[slen / 8 + 1]; String s; int c = 0; // 循環(huán),每次得到一個(gè)8位的01串 while (str.length() >= 8) { // 得到8位01串 s = str.substring(0, 8); BigInteger bi = new BigInteger(s, 2);// 將01串轉(zhuǎn)換為BigInteger類型 String s1 = bi.toString(10);// 轉(zhuǎn)換為10進(jìn)制結(jié)果 int i = Integer.valueOf(s1); by[c] = (byte) i; strlist1.put(by[c], s); // 將得到的8位01串丟掉. str = str.substring(8); c++; } by[c] = 0; dos.write(by); } else {// 如果字符串長(zhǎng)度不是8的整數(shù)倍,則說(shuō)明要多留出一位來(lái)存放那個(gè)不滿8zz位的"字節(jié)",同時(shí)還要多一位來(lái)存放補(bǔ)上的0的個(gè)數(shù). dos.writeInt(0);// 字符串的長(zhǎng)度不是8的整數(shù)倍 by = new byte[slen / 8 + 2]; String s; int c = 0; // 循環(huán),每次得到一個(gè)8位的01串 while (str.length() > 8) { // 得到8位01串 s = str.substring(0, 8); BigInteger bi = new BigInteger(s, 2);// 將01串轉(zhuǎn)換為BigInteger類型 String s1 = bi.toString(10);// 轉(zhuǎn)換為10進(jìn)制結(jié)果 int i = Integer.valueOf(s1); by[c] = (byte) i; strlist1.put(by[c], s); // 將得到的8位01串丟掉. str = str.substring(8); c++; } // 往字符串后面補(bǔ)0. int sl = str.length(); for (int k = 0; k < 8 - sl; k++) { str += 0; } BigInteger bi = new BigInteger(str, 2);// 將01串轉(zhuǎn)換為BigInteger類型 String str1 = bi.toString(10);// 轉(zhuǎn)換為10進(jìn)制結(jié)果 int i = Integer.valueOf(str1); // 將字符串轉(zhuǎn)成int類型 by[c] = (byte) i; // 強(qiáng)制轉(zhuǎn)型成byte類型.放入數(shù)組,寫到文件中. strlist1.put(by[c], str); by[c + 1] = (byte) (8 - sl); dos.write(by); } // 包裝成對(duì)象輸入流將碼表直接以對(duì)象的形式寫入文件 ObjectOutputStream oos; oos = new ObjectOutputStream(fos); oos.writeObject(writemap); oos.writeObject(strlist1); oos.flush(); // 強(qiáng)制輸出 dos.flush(); fos.close();
?由于上次在實(shí)現(xiàn)自定義畫(huà)板的文件保存時(shí),用了對(duì)象數(shù)據(jù)流, 嘗到了甜頭.. 于是我這次的碼表就直接用對(duì)流輸出流來(lái)寫.. 這個(gè)方法雖然省事.. 但是會(huì)產(chǎn)生"副作用":會(huì)降低壓縮比率.. 貌似對(duì)讀寫的時(shí)間也有影響..
?
?
接下來(lái)就是解壓了.. 其實(shí)就是壓縮的逆過(guò)程吧,, 只要好好注意.. 自己是怎么樣把各個(gè)字節(jié)寫入的,再一步一步將其還原回來(lái)就是了.
?
// 得到文件地址 FileInputStream fis = new FileInputStream(src); FileOutputStream fos = new FileOutputStream(des); // 包裝成數(shù)據(jù)流 DataInputStream dis = new DataInputStream(fis); DataOutputStream dos = new DataOutputStream(fos); int arraylen; // 讀取字節(jié)數(shù)組的長(zhǎng)度 arraylen = dis.readInt(); int flag = dis.readInt();// 此處a為標(biāo)志,1表示被壓縮的源文件的哈夫曼編碼總長(zhǎng)度是8的整數(shù)倍 // 0表示被壓縮的源文件的哈夫曼編碼的總長(zhǎng)度不是8的整數(shù)倍 // 被壓縮文件的源文件的哈夫曼編碼長(zhǎng)度是8的整數(shù)倍,即只多了一位放0.(arraylen==slen%8+1) if (flag == 1) { by = new byte[arraylen - 1];// 最后一位直接丟棄 dis.read(by); dis.read(); ObjectInputStream ois = new ObjectInputStream(fis); maps = (HashMap) ois.readObject(); m = (HashMap) ois.readObject(); // 將字節(jié)數(shù)組轉(zhuǎn)成字符串 String s1 = ""; for (int k = 0; k < by.length; k++) { s1 += m.get(by[k]); } String s2; int s2l = 0; int sl = 1; int s1l = s1.length(); while (s1l > 0) { // 首先從一位開(kāi)始找匹配,找到就寫文件 s2 = s1.substring(s2l, sl); while (maps.get(s2) == null) { sl++; s2 = s1.substring(s2l, sl); } dos.write(maps.get(s2)); s1 = s1.substring(sl); s2l = 0; sl = 1; s1l = s1.length(); } } else {// 不是8的整數(shù)倍..(arraylen==slen%8+1) by = new byte[arraylen]; dis.read(by); byte num = dis.readByte();// 讀出最后一個(gè)記錄補(bǔ)0個(gè)數(shù)的字節(jié) ObjectInputStream ois = new ObjectInputStream(fis); maps = (HashMap) ois.readObject(); m = (HashMap) ois.readObject(); String s = ""; for (int k = 0; k < by.length; k++) { s += m.get(by[k]); } int initlen = s.length(); s = s.substring(0, initlen - (int) num);// 截取第一位到補(bǔ)0的第一位. String s2; int s2l = 0; int sl = 1; int s1l = s.length(); while (s1l > 0) { // 首先從一位開(kāi)始找匹配,找到就寫文件 s2 = s.substring(s2l, sl); while (maps.get(s2) == null) { sl++; s2 = s.substring(s2l, sl); } dos.write(maps.get(s2)); s = s.substring(sl); s2l = 0; sl = 1; s1l = s.length(); } } dos.flush(); fos.close();
?
鑒于壓縮一個(gè)大文件實(shí)在是太慢了.. 就選了一個(gè)比較小的文件來(lái)示例了.. 壓縮的比率也的確不高啊....
?
?
?
然后就是利用系統(tǒng)提供的一個(gè)類.寫了個(gè)壓縮成zip格式的文件.? 壓縮完了之后直能用zip格式解壓器就能打開(kāi).像winRAR就能直接打開(kāi)查看..? 用了這個(gè)類... 代碼量少了不止是一行兩行,, 壓縮的速度.. 壓縮的比率... 唉 , 看得人糾結(jié)啊.. 學(xué)無(wú)止境呀,還有很多東西需要好好努力去學(xué)..
不過(guò)這個(gè)方法暫時(shí)還有點(diǎn)小問(wèn)題沒(méi)解決..? 中文名字亂碼!!? 這是java使用的是unicode編碼.. 而winRAR卻不是.. 所以才導(dǎo)致了這個(gè)問(wèn)題.. 這個(gè)問(wèn)題還真讓我有點(diǎn)蛋疼,, 實(shí)在不行的話就自己寫個(gè)類來(lái)解壓吧..呵呵..
具體實(shí)現(xiàn)暫時(shí)還只寫了個(gè)壓縮的方法. 并且只給了固定的地址的壓縮.
更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號(hào)聯(lián)系: 360901061
您的支持是博主寫作最大的動(dòng)力,如果您喜歡我的文章,感覺(jué)我的文章對(duì)您有幫助,請(qǐng)用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點(diǎn)擊下面給點(diǎn)支持吧,站長(zhǎng)非常感激您!手機(jī)微信長(zhǎng)按不能支付解決辦法:請(qǐng)將微信支付二維碼保存到相冊(cè),切換到微信,然后點(diǎn)擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對(duì)您有幫助就好】元
