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

