1.用来构造赫夫曼树的一个类
Node.java
public class Node implements Comparable<Node> {
Byte data;
int value;
Node left;
Node right;
public Node(Byte data, int value) {
super();
this.data = data;
this.value = value;
}
@Override
public String toString() {
return "Node [data=" + data + ", value=" + value + "]";
}
public void list(Node root) {
if (root != null) {
root.before();
} else {
System.out.println("树为空,不能遍历!");
}
}
public void before() {
System.out.println(this);
if (this.left != null) {
this.left.before();
}
if (this.right != null) {
this.right.before();
}
}
@Override
public int compareTo(Node o) {
return this.value - o.value;
}
}
2.利用赫夫曼编码进行压缩的一个方法类
zip.java
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class Zip {
//用来存放赫夫曼编码
static HashMap <Byte, String>huffmanCodes=new HashMap<Byte, String>();
//进行字符串拼接
static StringBuilder stringBuilder=new StringBuilder();
//5.把所有方法封装到一块方便调用
public byte[] hufumanzip(byte[] bytes) {
List<Node> hufullList = hufullList(bytes);
//根据hufullList创建哈夫曼树
Node findNode = findNode(hufullList);
//对应的哈夫曼编码
HashMap<Byte, String> codes = getCodes(findNode);
//根据赫夫曼编码,得到压缩后的哈夫曼编码
byte[] zip = zip(bytes, codes);
return zip;
}
// 1.接收一个字符数组并统计每个字符出现的次数
public List<Node> hufullList(byte[] bytes) {
// 创建链表用于存储结点
ArrayList<Node> list = new ArrayList<Node>();
// 创建哈希表存放带有键值的结点
HashMap<Byte, Integer> map = new HashMap<>();
for (byte b : bytes) {
// 获取键值的value值
Integer count = map.get(b);
// 对value值进行计数
if (count == null) {
map.put(b, 1);
} else {
map.put(b, count+1);
}
}
// 对哈希表进行遍历取出键值对
for (Map.Entry<Byte, Integer> entry : map.entrySet()) {
list.add(new Node(entry.getKey(), entry.getValue()));
}
return list;
}
// 2.转化成哈夫曼树
public Node findNode(List<Node> list) {
while (list.size() > 1) {
Collections.sort(list);
Node leftNode = list.get(0);
Node rightNode = list.get(1);
Node root = new Node(null, leftNode.value + rightNode.value);
root.left = leftNode;
root.right = rightNode;
list.remove(leftNode);
list.remove(rightNode);
list.add(root);
}
return list.get(0);
}
//3.重载getCodes()方法方便调用
public HashMap<Byte, String> getCodes(Node node) {
if (node==null) {
return null;
}
getCodes(node, "", stringBuilder);
return huffmanCodes;
}
//3.创建哈夫曼表每个字符用它对应的哈夫曼编码表示(每个叶子结点的路径码)
public void getCodes(Node node,String code,StringBuilder stringBuilder) {
StringBuilder builder=new StringBuilder(stringBuilder);
builder.append(code);
if (node!=null) {//为空结点
//判断是叶子节点还是非叶子结点
if (node.data==null) {
//向左递归处理
getCodes(node.left, "0", builder);
//向右递归
getCodes(node.right, "1", builder);
}else {
//表示到达叶子结点,放入map中
huffmanCodes.put(node.data, builder.toString());
}
}
}
//4.对哈夫曼编码转化为byte字符进行压缩
/**
* @param bytes 原始字符串对应的byte[]
* @param hufullmancodes 生成哈夫曼编码map
* @return 返回赫夫曼编码后的byte[];
*/
public byte[] zip(byte[] bytes,HashMap <Byte, String>hufullmancodes) {
StringBuilder stringBuilder=new StringBuilder();
for (byte b : bytes) {
stringBuilder.append(hufullmancodes.get(b));
}
//System.out.println("赫夫曼编码"+stringBuilder);
//return bytes;
//统计返回的byte[] 的长度
int len;
if (stringBuilder.length()%8==0) {
len=stringBuilder.length()/8;
}else {
len=stringBuilder.length()/8+1;
}
//创建压缩后的byte数组
byte[] by=new byte[len];
//因为一个八位的二进制数构成一个byte字节
int index=0;//记录byte[]数组的下标
for (int i = 0; i < stringBuilder.length(); i+=8) {
String strByte;
if (i+8>stringBuilder.length()) {//不够八位
strByte=stringBuilder.substring(i);
}else {
strByte=stringBuilder.substring(i,i+8);
}
//将strByte转成一个byte,放入到by;
by[index]=(byte) Integer.parseInt(strByte,2);
index++;
}
return by;
}
}
3.对文件解压的一个方法类
Unzip.java
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class Unzip {
/**
* @param flag 标志是否需要补高位如果是true,表示需要,false表示否.最后一个字符无需补高位
* @param b传入的byte
* @return 是该b对应的二进制的字符串(按补码返回)
*/
public String byteTobitString(boolean flag, byte b) {
// 使用变量保存b
int temp = b; // 将b转成int
// 如果是正数我们还存在补高位
if (flag) {
temp |= 256; // 按位或256 1 0000 0000 | 0000 0001=> 1 0000 0001
}
String str = Integer.toBinaryString(temp);// 返回的是temp对应的二进制的补码
if (flag) {
return str.substring(str.length() - 8);
} else {
return str;
}
}
/**
* @param huffmancodes 赫夫曼编码表map 32=01, 97=100, 100=11000, 117=11001, 101=1110,
* @param huffmanbytes 赫夫曼编码得到的字节数组 -88, -65, -56, -65, -56, -65, -55, 77, -57,
* 6, -24, -14,
* @return
*/
public byte[] decode(HashMap<Byte, String> huffmancodes, byte[] huffmanbytes) {
// 先得到huffmanbytes对应的二进制的字符串
StringBuilder stringBuilder = new StringBuilder();
// 将byte数组转成二进制的字符串
for (int i = 0; i < huffmanbytes.length; i++) {
byte b = huffmanbytes[i];
// 判断是不是最后一个字节
boolean flag = (i == huffmanbytes.length - 1);
stringBuilder.append(byteTobitString(!flag, b));
}
// System.out.println(stringBuilder.toString());
// 把字符串按照指定的赫夫曼编码进行解码
// 把赫夫曼编码来进行调换,因为反向查询
HashMap<String, Byte> hashMap = new HashMap<String, Byte>();
for (Map.Entry<Byte, String> entry : huffmancodes.entrySet()) {
hashMap.put(entry.getValue(), entry.getKey());
}
// System.out.println(hashMap);
// 创建集合,存放byte;
List<Byte> list = new ArrayList<>();
// i可以理解为是索引,扫描stringBuilder
for (int i = 0; i < stringBuilder.length();) {
int count = 1; // 计数器
boolean flag = true;
Byte b = null;
while (flag) {
// 取出一个‘1’‘0’
String key = stringBuilder.substring(i, i + count);// i不动,让count移动,直到匹配到一个字符
b = hashMap.get(key);
if (b == null) { // 说明没有匹配到
count++;
} else {
flag = false;
}
}
list.add(b);
i += count; // i直接移动到count
}
// for循环结束后,我们list中就存放了所有的字符
// 把list中的数据放入到byte[] 并返回
byte[] b = new byte[list.size()];
for (int i = 0; i < b.length; i++) {
b[i] = list.get(i);
}
return b;
}
}
4.调用各种流读取文件并进行压缩
Filezip.java
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
public class Filezip {
public void filezip(String srcFile, String dstFile) throws Exception {
//创建文件输入流
FileInputStream is = new FileInputStream(srcFile);
//创建byte数组;
byte[] bytes=new byte[is.available()];
is.read(bytes);
Zip zip = new Zip();
byte[] hufumanzip = zip.hufumanzip(bytes);
//创建文件输出流
FileOutputStream os=new FileOutputStream(dstFile);
//创建一个对象输出流
ObjectOutputStream oos=new ObjectOutputStream(os);
//把赫夫曼编码后的字节数组写入压缩文件
oos.writeObject(hufumanzip);
//把赫夫曼编码写入压缩文件
oos.writeObject(Zip.huffmanCodes);
is.close();
os.close();
oos.close();
}
}
5.调用各种流对文件进行解压
Fileunzip.java
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.util.HashMap;
public class Fileunzip {
public void unzip(String zipfile, String srcfile) throws Exception {
FileInputStream is = new FileInputStream(zipfile);
ObjectInputStream os = new ObjectInputStream(is);
// 读取赫夫曼字节数组
byte[] huffmanBytes = (byte[]) os.readObject();
// 读取赫夫曼编码表
HashMap<Byte, String> huffmanCodes = (HashMap<Byte, String>) os.readObject();
Unzip jieya = new Unzip();
// 解码
byte[] decode = jieya.decode(huffmanCodes, huffmanBytes);
// 创建输出流
FileOutputStream fos = new FileOutputStream(srcfile);
// 把文件写入到指定位置
fos.write(decode);
is.close();
os.close();
fos.close();
}
}
6.测试类
Filetext.java
public class Filetext {
public static void main(String[] args) throws Exception {
Filezip filezip = new Filezip();
String s1="E:\\1.txt";
String s2="E:\\2.zip";
filezip.filezip(s1, s2);
System.out.println("压缩成功");
// Fileunzip fileunzip = new Fileunzip();
// String sr1="E:\\2.zip";
// String sr2="E:\\3.txt";
// fileunzip.unzip(sr1, sr2);
// System.out.println("解压成功");
}
}
注意:
1.个别文件进行压缩效果可能不明显(可能会变大)
2.以上代码不能对文件夹进行压缩
3.压缩成zip文件时是不能通过电脑上的压缩软件进行解压,必须通过我们代码来解压。
4.该代码可以用来对文件进行加密处理
代码有啥可以优化和改进的地方,欢迎各位大神指出!