实现思路
huffmantree的创建、压缩和解压
创建思路
1、写一个方法得到对应的String的Byte数组
2、创建一个Node节点:
属性:data(每个数据的asc码) weight(数据出现的次数) left right
继承Comparable
方法:toString(重写toString) preOrder(前序遍历) comparableto(比较大小的方法)
3、方法:将Byte数组转化为map集合(key存储数据,value存储出现的次数)
4、将map集合转化为list
5、对list进行节点的huffmantree排序,使之只有一个节点
代码
package com.atguigu.huffmanCode;
import java.io.*;
import java.util.*;
/**
* huffmantree的创建、压缩和解压
*
*
* 创建思路
* 1、写一个方法得到对应的String的Byte数组
* 2、创建一个Node节点:
* 属性:data(每个数据的asc码) weight(数据出现的次数) left right
* 继承Comparable
* 方法:toString(重写toString) preOrder(前序遍历) comparableto(比较大小的方法)
* 3、方法:将Byte数组转化为map集合(key存储数据,value存储出现的次数)
* 4、将map集合转化为list
* 5、对list进行节点的huffmantree排序,使之只有一个节点
*/
public class Huffmancode {
static StringBuilder Strbul = new StringBuilder();
static Map<Byte,String> codes = new HashMap<Byte,String>();
public static void main(String[] args) {
//测试文件的压缩,外部文件
String inFile = "D:\\www.bmp";
String outFile = "D:\\qqq.zip";
zip(inFile,outFile);
System.out.println("压缩成功");
//解压文件
String zipFile = "D:\\qqq.zip";
String dstFile = "D:\\www2.bmp";
unZip(zipFile,dstFile);
System.out.println("解压成功");
/* java.lang.String datas = "i like like like java do you like a java";
byte[] overcodes = huffmanTreeBytes(datas);
System.out.println("经过压缩后结果为" + Arrays.toString(overcodes) + "\n长度为" + overcodes.length);
//解码
byte[] overbytes = decode(getCodes(datas),overcodes);
System.out.println("解码后的结果为" + new String(overbytes));
*/
// codesToString(huffmanTreeBytes(datas));//查看解码后的二进制编码101010111100111.这种形式
/*
// byte[] bytesDatas = StringTobyte(datas);
// System.out.println("初始的数据长度为" + bytesDatas.length);
//将字符串转化为链表形式的数据
List<Node> nodes = byteToMap(StringTobyte(datas));
// System.out.println(nodes);
//将哈夫曼树生成
Node root = createHuffmanTree(nodes);
System.out.println("root为" );
//前序遍历哈夫曼树
root.preOrder();
//生成的hufuman编码表为
System.out.println("huffman编码表为");
Map<Byte,String> codes = createcodes(root);
System.out.println(codes);*/
}
/**
* 用于解压文件
* @param zip
* @param dstFile
*/
public static void unZip(String zip,String dstFile){
//创建输入流
InputStream is = null;
//定义一个对象输入流
ObjectInputStream ois = null;
//定义一个文件的输出流
OutputStream os = null;
try {
//创建文件输入流
is = new FileInputStream(zip);
//创建一个和is关联的文件输入流
ois = new ObjectInputStream(is);
//读取Byte数组
byte[] bytes = (byte[]) ois.readObject();
// System.out.println("待解码的Byte数组为" + new String(bytes));
//读取霍夫曼编码表
Map<Byte,String> codes = (Map<Byte, String>) ois.readObject();
System.out.println("解码的map为" + codes);
//解码
byte[] overcode = new byte[2];//decode(codes,bytes);
//将bytes写入到目标文件
os = new FileOutputStream(dstFile);
os.write(overcode);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} finally {
try {
os.close();
ois.close();
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
/* *
* 用于解压文件
* @param zipFile 待解压的文件
* @param dstFile 解压完放入的路径
*/
public static void unZip2(String zipFile,String dstFile){
//创建输入流、输出流、输入流对象
InputStream is = null;
ObjectInputStream ois = null;
OutputStream os = null;
try {
//创建输入流
is = new FileInputStream(zipFile);
ois = new ObjectInputStream(is);
//读取被编码后的数组
byte[] bytes = (byte[]) ois.readObject();
//获取霍夫曼编码
Map<Byte,String> huffmanTreeCodes = (Map<Byte, String>) ois.readObject();
//解码
byte[] overbytes = new byte[2];//decode(huffmanTreeCodes,bytes);
//创建输出对象
os = new FileOutputStream(dstFile);
//输出到文件
os.write(overbytes);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} finally {
try {
os.close();
} catch (IOException e) {
System.out.println("关闭os错误");
e.printStackTrace();
}
try {
ois.close();
} catch (IOException e) {
System.out.println("关闭ois错误");
e.printStackTrace();
}
try {
is.close();
} catch (IOException e) {
System.out.println("关闭is错误");
e.printStackTrace();
}
}
}
/**
* 用以压缩文件的方法
* @param srcFile 等待压缩的文件路径
* @param dstFile 压缩完毕输出的路径
*/
public static void zip(String srcFile,String dstFile){
//创建输入流、输出流
FileInputStream is = null;
FileOutputStream os = null;
ObjectOutputStream oos = null;
try {
//创建输入流
is = new FileInputStream(srcFile);
//创建临时存储的数组
byte[] bytes = new byte[is.available()];
//将文件写入到临时数组中
is.read(bytes);
//压缩
byte[] overfile = huffmanTreeBytes(bytes);
//写入到输出流
os = new FileOutputStream(dstFile);
// os.write(overfile);
//将霍夫曼编码写入到输出流
oos = new ObjectOutputStream(os);
oos.writeObject(overfile);
oos.writeObject(getCodes(bytes));
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
is.close();
os.close();
oos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 将byte数组解码的方法
* @param huffmantree 霍夫曼编码
* @param codes 待解码的数组
* @return 返回的结果
*/
public static byte[] decode(Map<Byte,String> huffmantree , byte[] codes){
//用于解码的map
Map<String,Byte> decodeMap = new HashMap<String,Byte>();
for (Map.Entry<Byte,String> entry:huffmantree.entrySet()) {
decodeMap.put(entry.getValue(),entry.getKey());
}
// System.out.println("反编码的集合为" + decodeMap);\
//overCodes 解码后的二进制数据0101101010
String overCodes = codesToString(codes);
List<Byte> decode = new ArrayList<>();//结果
for (int i = 0; i < overCodes.length(); ) {
boolean flag = true;//临时变量
int count = 1;
Byte b = null;
while(flag){
String key = overCodes.substring(i,i + count);
b = decodeMap.get(key);
if (b == null){
count++;
}else {
flag = false;
}
}
decode.add(b);
i += count;
}
byte[] overbytes = new byte[decode.size()];
for (int i = 0; i < decode.size(); i++) {
overbytes[i] = decode.get(i);
}
// System.out.println("解码后的结果为" + new String(overbytes));
return overbytes;
}
/**
* 将压缩后的byte数组转化为字符串
* @param codes byte数组
* @return 返回字符串
*/
public static String codesToString(byte[] codes){
StringBuilder str = new StringBuilder();
for (int i = 0; i < codes.length; i++) {
if ( i == codes.length - 1){
str.append(byteToBitString(false,codes[i]));
}else{
str.append(byteToBitString(true,codes[i]));
}
}
// System.out.println("解码后的二进制数据为为" + str.toString());
return str.toString();
}
/**
* 将byte转化为字符串,即转化为100101010...的形式
* @param b 传入的数据
* @param flag 当为正数或者最后一位
* @return 返回一个字符串
*/
public static String byteToBitString(boolean flag,byte b){
int temp = b;
if (flag){
temp |= 256;
}
String str = Integer.toBinaryString(temp);
if (flag){
return str.substring(str.length() - 8 );
}else{
return str;
}
}
public static byte[] huffmanTreeBytes(byte[] bytes){
int overByteLen = 0;
//用来拼接临时编码值的
StringBuilder strbul = new StringBuilder();
//获得编码
Map<Byte,String> codes = getCodes(bytes);
//将数组中的所有值对应的哈夫曼编码拼接
for (byte value: bytes) {
strbul.append(codes.get(value));
}
//创建结果的Byte数组
byte[] overBytes = new byte[(strbul.length() + 7) / 8 ];
int index = 0;
//将编码转换为二进制的数据
for (int i = 0; i < strbul.length(); i = i + 8) {
String momentstr;
if ((i + 8) > strbul.length()){
momentstr = strbul.substring(i);
}else{
momentstr = strbul.substring(i,i + 8);
}
// overBytes[index++] = Byte.parseByte(momentstr,2); 不可以这样写,会越界
overBytes[index++] = (byte) Integer.parseInt(momentstr,2);
}
return overBytes;
}
//将String字符串按照编码规则转化为编码
public static byte[] huffmanTreeBytes(String str){
int overByteLen = 0;
//用来拼接临时编码值的
StringBuilder strbul = new StringBuilder();
//bytes 表示将String每一个字符转化为了数组中的值
byte[] bytes = StringTobyte(str);
//获得编码
Map<Byte,String> codes = getCodes(str);
//将数组中的所有值对应的哈夫曼编码拼接
for (byte value: bytes) {
strbul.append(codes.get(value));
}
//创建结果的Byte数组
byte[] overBytes = new byte[(strbul.length() + 7) / 8 ];
int index = 0;
//将编码转换为二进制的数据
for (int i = 0; i < strbul.length(); i = i + 8) {
String momentstr;
if ((i + 8) > strbul.length()){
momentstr = strbul.substring(i);
}else{
momentstr = strbul.substring(i,i + 8);
}
// overBytes[index++] = Byte.parseByte(momentstr,2); 不可以这样写,会越界
overBytes[index++] = (byte) Integer.parseInt(momentstr,2);
}
return overBytes;
}
/**
* 获得编码的方法
* @param str 传入的字符串
* @return 为传入字符串对应的哈希编码
*/
public static Map<Byte,String> getCodes(String str){
//bytes 表示将String每一个字符转化为了数组中的值
byte[] bytes = StringTobyte(str);
//将字符串数组转化为链表
List<Node> nodes = byteToMap(bytes);
//创建哈希曼树
Node root = createHuffmanTree(nodes);
return createcodes(root);
}
/**
* 获得编码的方法
* @param bytes 传入的数组
* @return 为传入字符串对应的霍夫曼编码
*/
public static Map<Byte,String> getCodes(byte[] bytes){
//将字符串数组转化为链表
List<Node> nodes = byteToMap(bytes);
//创建哈希曼树
Node root = createHuffmanTree(nodes);
return createcodes(root);
}
//获得哈夫曼编码
public static Map<Byte,String> createcodes(Node root){
if (root != null){
return createcodes(root,"",Strbul);
}else {
return null;
}
}
public static Map<Byte,String> createcodes(Node root,String code,StringBuilder Strbul){
StringBuilder StrBul2 = new StringBuilder(Strbul);
StrBul2.append(code);
if (root != null){
if (root.data == null){
createcodes(root.left,"0",StrBul2);
createcodes(root.right,"1",StrBul2);
}else {
codes.put(root.data,StrBul2.toString());
}
}
return codes;
}
//创建赫夫曼树
public static Node createHuffmanTree(List<Node> nodes){
while(nodes.size() >1){
//1、排序
Collections.sort(nodes);
//2、取出两个最小的
Node leftNode = nodes.get(0);
Node rightNode = nodes.get(1);
//3、创建新节点
Node parent = new Node(null,leftNode.weight + rightNode.weight);
parent.left = leftNode;
parent.right = rightNode;
//4、删除节点
nodes.remove(leftNode);
nodes.remove(rightNode);
//5、添加节点
nodes.add(parent);
}
return nodes.get(0);
}
//将String转化为byte 数组
public static byte[] StringTobyte(String str){
return str.getBytes();
}
//将byte数组转化为集合
public static List<Node> byteToMap(byte[] bytes){
List<Node> nodes = new ArrayList<Node>();
Map<Byte,Integer> count = new HashMap<>();
for (int i = 0; i < bytes.length; i++) {
Integer times = count.get(bytes[i]);
if (count.get(bytes[i]) == null) {
count.put(bytes[i], 1);
} else {
count.put(bytes[i], times + 1);
}
}
for (Map.Entry<Byte,Integer> entry:count.entrySet()) {
nodes.add(new Node(entry.getKey(),entry.getValue()));
}
return nodes;
}
//节点的前序遍历
public static void perOrder(Node root){
if (root != null){
root.preOrder();
}else{
System.out.println("树为空");
}
}
}
/**
*
*/
class Node implements Comparable<Node>{
Byte data;
int weight;
Node left;
Node right;
public void preOrder(){
System.out.println(this);
if (this.left != null){
this.left.preOrder();
}
if (this.right != null){
this.right.preOrder();
}
}
@Override
public int compareTo(Node o) {
return this.weight - o.weight;
}
@Override
public String toString() {
return "Node{" +
"data=" + data +
", weight=" + weight +
'}';
}
public Node(Byte data, int weight) {
this.data = data;
this.weight = weight;
}
}