目录
压缩思路:
1.先将要压缩的句子或者文件内容存入一个byte数组中 2.处理字符串,存入哈希表<byte值,出现频率> 3.根据字符串处理后的哈希表创建一个哈夫曼树,字符都是叶子节点,出现频率越高,距离根结点越近 4.生成哈夫曼编码表,保存在哈希表中<Byte值,编码>,比如字母i(本身有一个byte)会对应一个比如001这样的编码。 byte值对应的编码是根据从根结点到该节点的路径,往左走一步在编码后面加个“0”,右走加“1” 5.根据哈夫曼编码表得到字符串对应的哈夫曼编码 要注意的是,这里得到的是2进制的编码,这样的压缩率不会太高,因此要将其转化为十进制存入byte数组(每个byte可以存8个二进制编码)
解压思路:
1.将压缩文件中的两个对象(哈夫曼编码表和文件编码)拿出来 2.处理存储哈夫曼编码的哈希表(反转:<Byte,String>-><String,Byte>)方便后面根据String查找Byte 3.根据反转后的哈希表和压缩文件编码,变量编码找出对应的字符。
注意:
由于我们要将压缩规则一并传入压缩文件中,所以压缩后的文件不一定比原文件小,但如果我们只传入压缩后的数据,压缩率会很高,但由于我们的压缩规则是根据数据得到的,因此无法解压。
代码:
package ZipFile;
import java.io.*;
import java.util.*;
import java.util.List;
/*
压缩思路:
1.先将要压缩的句子或者文件内容存入一个byte数组中
2.处理字符串,存入哈希表<byte值,出现频率>
3.根据字符串处理后的哈希表创建一个哈夫曼树,字符都是叶子节点,出现频率越高,距离根结点越近
4.生成哈夫曼编码表,保存在哈希表中<Byte值,编码>,比如字母i(本身有一个byte)会对应一个比如001这样的编码。
byte值对应的编码是根据从根结点到该节点的路径,往左走一步在编码后面加个“0”,右走加“1”
5.根据哈夫曼编码表得到字符串对应的哈夫曼编码
要注意的是,这里得到的是2进制的编码,这样的压缩率不会太高,因此要将其转化为十进制存入byte数组(每个byte可以存8个二进制编码)
*/
/*
解压思路:
1.将压缩文件中的两个对象(哈夫曼编码表和文件编码)拿出来
2.处理存储哈夫曼编码的哈希表(反转:<Byte,String>-><String,Byte>)方便后面根据String查找Byte
3.根据反转后的哈希表和压缩文件编码,变量编码找出对应的字符。
*/
public class ZipFileTest {
public static void main(String[] args) {
ZipTool zipTool=new ZipTool();
zipTool.zipSentence("Go and just do it. ");
zipTool.zipFile("C:\\Users\\lenovo\\Desktop\\123.doc","C:\\Users\\lenovo\\Desktop\\yasuo6.zip");
zipTool.unZipFile("C:\\Users\\lenovo\\Desktop\\yasuo6.zip","C:\\Users\\lenovo\\Desktop\\1234.doc");
}
}
//前三个方法是封装的功能方法,后面先是压缩用到的方法,最后是解压用到的方法
class ZipTool{
//这个方法是压缩一句话(可以用来测试压缩率),具体方法的功能参考压缩文件的方法
public void zipSentence(String s){
byte[] bytes= s.getBytes();
System.out.println(bytes.length);
Map<Byte,Integer> map=dealString(bytes);
Node node=getHuffmanTree(map);
Map<Byte,String> huffmanCode=getHuffmanCodeTable(node);
byte[] result=getHuffmanCode(bytes,huffmanCode);
System.out.println(result.length);
System.out.println(Arrays.toString(result));
}
//这个方法是压缩文件
public void zipFile(String source,String pack){
FileInputStream fileInputStream=null;
FileOutputStream fileOutputStream=null;
ObjectOutputStream oos=null;
try {
fileInputStream=new FileInputStream(source);
fileOutputStream=new FileOutputStream(pack);
//创建一个大小和文件一样的byte数组,available返回还没读的字节数
byte[] bytes=new byte[fileInputStream.available()];
//将文件内容读入bytes数组
fileInputStream.read(bytes);
//处理byte数组,存入哈希表<byte值,出现频率>
Map<Byte,Integer> map=dealString(bytes);
//根据字符串处理后的哈希表创建一个哈夫曼树,字符都是叶子节点,出现频率越高,距离根结点越近
Node node=getHuffmanTree(map);
//生成哈夫曼编码表,保存在哈希表中<Byte值,编码>,比如字母i(本身有一个byte)会对应一个比如001这样的编码。
Map<Byte,String> huffmanCode=getHuffmanCodeTable(node);
byte[] result=getHuffmanCode(bytes,huffmanCode);
//这里要以对象的形式将压缩规则(哈夫曼编码表)和压缩的数据传入压缩文件中,方面后面提取规则和数据
oos=new ObjectOutputStream(fileOutputStream);
oos.writeObject(huffmanCode);
oos.writeObject(result);
oos.flush();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
if(fileInputStream!=null){
try {
fileInputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(oos!=null){
try {
oos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
//这个方法是解压的方法
public void unZipFile(String pack,String source){
FileInputStream fileInputStream=null;
FileOutputStream fileOutputStream=null;
ObjectInputStream objectInputStream=null;
try {
fileOutputStream=new FileOutputStream(source);
fileInputStream=new FileInputStream(pack);
objectInputStream=new ObjectInputStream(fileInputStream);
//按顺序取出哈夫曼编码和压缩的数据
Map<Byte,String> huffmanCode=(Map<Byte, String>) objectInputStream.readObject();
byte[] bytes=(byte[]) objectInputStream.readObject();
//处理哈夫曼编码反转为<编码,Byte值>来方便后面根据编码对照着找byte值
Map<String,Byte> mapDeal=unZipDeal(huffmanCode);
byte[] result=unZip(mapDeal,bytes);
fileOutputStream.write(result);
fileOutputStream.flush();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}finally {
if(objectInputStream!=null){
try {
objectInputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(fileOutputStream!=null){
try {
fileOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
//处理字符串,存入哈希表<byte值,出现频率>
public Map<Byte,Integer> dealString(byte[] bytes){
Map<Byte,Integer> hashMap=new HashMap<>();
int i=0;
while(i<bytes.length){
hashMap.put(bytes[i],hashMap.getOrDefault(bytes[i],0)+1);
i++;
}
return hashMap;
}
//下面为压缩用到的方法-----------------------------------------------------------------
//根据字符串处理后的哈希表创建一个哈夫曼树
public Node getHuffmanTree(Map<Byte,Integer> map){
List<Node> list=new Stack<>();
for(Map.Entry<Byte,Integer> entry:map.entrySet()){
Node node=new Node(entry.getKey(),entry.getValue());
list.add(node);
}
while(list.size()>1) {
Collections.sort(list);
//System.out.println(list.toString());
Node node1 = list.get(0);
Node node2 = list.get(1);
Node newNode = new Node(null, node1.weight + node2.weight);
newNode.leftNode=node1;
newNode.rightNode=node2;
list.remove(0);
list.remove(0);
list.add(newNode);
}
return list.get(0);
}
//生成哈夫曼编码表,保存在哈希表中<Byte值,编码>
public Map<Byte,String> getHuffmanCodeTable(Node node){
Map<Byte,String> huffmanCode=new HashMap<>();
StringBuilder s=new StringBuilder("");
getHuffmanCodeTable(node,huffmanCode,s);
/* for(Map.Entry<Byte,StringBuilder> entry:huffmanCode.entrySet()){
System.out.println(entry.getKey()+"="+entry.getValue());
}*/
return huffmanCode;
}
//得到哈夫曼编码表
public void getHuffmanCodeTable(Node node,Map<Byte,String> huffmanCode,StringBuilder s) {
//System.out.println(node);
if(node!=null){
if(node.c!=null){
huffmanCode.put(node.c,s.toString());
return;
}
StringBuilder stringBuilderL=new StringBuilder(s);
getHuffmanCodeTable(node.leftNode,huffmanCode,stringBuilderL.append("0"));
StringBuilder stringBuilderR=new StringBuilder(s);
getHuffmanCodeTable(node.rightNode,huffmanCode,stringBuilderR.append("1"));
}
return;
}
//根据哈夫曼编码表得到字符串对应的哈夫曼编码
public byte[] getHuffmanCode(byte[] bytes,Map<Byte,String> huffmanCodeTable){
StringBuilder huffmanCode=new StringBuilder();
for(byte b:bytes){
huffmanCode.append(huffmanCodeTable.get(b));
}
/* System.out.println(huffmanCode.length());
System.out.println(huffmanCode.toString());*/
int len=(huffmanCode.length()+7)/8;
byte[] huffmanCodeByte=new byte[len];
//i表示字符串生成的哈夫曼编码的索引,index表示存入的byte[]的索引
for(int i=0,index=0;i<huffmanCode.length();i+=8){
String temp;
if(i+8>huffmanCode.length()){
temp=huffmanCode.substring(i);
}else{
//System.out.println(huffmanCode.substring(i,i+8).toString()+" "+Integer.parseInt(huffmanCode.substring(i,i+8).toString()));
temp=huffmanCode.substring(i,i+8);
}
//System.out.println("temp:"+temp);
//这里要将二进制转化为10进制存入byte数组中(来节省空间),所以要通过Integer中的parseInt传参使2进制转化为10进制。
huffmanCodeByte[index]=(byte) Integer.parseInt(temp,2);
index++;
}
return huffmanCodeByte;
//return huffmanCode.toString();
}
//下面为解压用到的方法-----------------------------------------------------------------
//处理存储哈夫曼编码的哈希表(反转:<Byte,String>-><String,Byte>)方便后面根据String查找Byte
/*
* @param * @Param huffmanCode: 读取的哈夫曼编码表
* @return * @return: java.util.Map<java.lang.String,java.lang.Byte> 反转的哈夫曼编码
*/
public Map<String,Byte> unZipDeal(Map<Byte,String> huffmanCode){
Map<String,Byte> dealHuffmanCode=new HashMap<>();
for(Map.Entry<Byte,String> code:huffmanCode.entrySet()){
dealHuffmanCode.put(code.getValue(),code.getKey());
}
return dealHuffmanCode;
}
/*
* @param * @Param dealHuffmanCode: <String,Byte>这样的哈希表(译码表)
* @Param bytes: 从压缩文件中读取的byte数组
* @return * @return: byte[] 解压后的byte数组
*/
public byte[] unZip(Map<String,Byte> dealHuffmanCode,byte[] bytes){
StringBuilder stringBuilder=new StringBuilder();//存储bytes数组转化为的二进制码
boolean flag=true;
StringBuilder huffmanString=new StringBuilder();
for(int i=0;i<bytes.length;i++){
int temp=bytes[i];
if(i==bytes.length-1&&temp>=0){
String s=Integer.toBinaryString(temp);
huffmanString.append(s);
}else{
temp|=256;
String s=Integer.toBinaryString(temp);
huffmanString.append(s.substring(s.length()-8));
}
}
List<Byte> list=new ArrayList<>();
for(int i=0;i<huffmanString.length();){
int temp=i;
while(true){
temp++;
if(dealHuffmanCode.get(huffmanString.substring(i,temp))!=null){
list.add(dealHuffmanCode.get(huffmanString.substring(i,temp)));
break;
}
}
i=temp;
}
byte[] huffmanByte=new byte[list.size()];
for(int i=0;i<huffmanByte.length;i++){
huffmanByte[i]=list.get(i);
}
return huffmanByte;
}
}
class Node implements Comparable<Node>{
Byte c;
int weight;
Node leftNode;
Node rightNode;
public Node(Byte c, int weight) {
this.c = c;
this.weight = weight;
}
@Override
public String toString() {
return "Node{" +
"c=" + c +
", weight=" + weight +
'}';
}
@Override
public int compareTo(Node o) {
return this.weight-o.weight;
}
}