在当今信息爆炸时代,如何采用有效的数据压缩技术来节省数据文件的存储空间越来越引起人们的重视。哈夫曼编码正是一种应用广泛且非常有效的数据压缩技术。我们要分析与设计哈夫曼树的存储结构,实现哈夫曼算法以及编码与译码基本功能,并对文本文件利用哈夫曼编码进行压缩得到压缩文件,然后进行解压缩得到解压文件。进行此项设计要掌握二叉树、哈夫曼树的概念,性质与存储结构,能够利用哈夫曼算法实现哈夫曼编码,并应用于文件压缩。
在数据存储中,假如文件过大存储可能会造成空间的浪费,并且上传文件时,文件的传输时间也会比较长。因此我们可以利用文件压缩来减小时间资源与空间资源的使用量。哈夫曼编码使用的是编码表来对原文件进行编码,将数据量减小,从而更加节约计算机的使用资源。
以下是代码:
import java.io.*;
import java.util.*;
public class hafuman {
static int last ;
static HashMap<Character,String> huffmanTreeMapCode =new HashMap<>(); //哈夫曼编码表
static StringBuilder stringBuilder =new StringBuilder();//字符串拼接类
public static void main(String[] args) throws IOException, ClassNotFoundException {
File file =new File("E:/java/Demo.txt"); //创建一个文件
if(!file.exists()) //如果没有则创建
file.createNewFile();
File file1 =new File("E:/java/zipDemo.zip");
if(!file.exists()) //如果没有则创建
file1.createNewFile();
File file2 =new File("E:/java/unzip.txt");
if(!file2.exists()) //如果没有则创建
file.createNewFile();
String srcFile ="E:/java/Demo.txt"; //原文件路径
String zipFile ="E:/java/zipDemo.zip";//压缩后的文件路径
String deZipFile ="E:/java/unzip.txt";//解压之后的文件路径
zipFile(srcFile,zipFile); //压缩文件
unZip(zipFile,deZipFile);//解压文件
}
public static void unZip(String zipFile,String dstFile) throws IOException, ClassNotFoundException
{
FileInputStream fis =new FileInputStream(zipFile);
byte[] bytes =new byte[fis.available()];
fis.read(bytes);
String s=Decode(bytes,huffmanTreeMapCode);
FileOutputStream fos =new FileOutputStream(dstFile);
fos.write(s.getBytes());
fis.close();
fos.close();
}
压缩文件
public static void zipFile(String srcFile,String dstFile) throws IOException
{
FileInputStream fis = new FileInputStream(srcFile);
byte[] bytes =new byte[fis.available()];
fis.read(bytes);
fis.close();
List<Node> list =getNode(new String(bytes));
Node root =CreateHuffmanTree(list);
getCode(root,"",stringBuilder);
HashMap<Character,String> huffmanTreeMapCode =new HashMap<>();
byte[] b=zip(huffmanTreeMapCode,new String(bytes));
FileOutputStream fos = new FileOutputStream(dstFile);
fos.write(b);
fos.close();
}
//第七步 对压缩成的byte数组进行解码
public static String Decode(byte[] bytes,Map<Character,String> huffmanTreeMapCode )
{
StringBuilder stringBuilder =new StringBuilder();
last =Integer.toBinaryString(bytes[bytes.length-1]).length();
for(int i=0;i<bytes.length;i++) {
if(i!=bytes.length-1) stringBuilder.append(byteToBinary(bytes[i], true));
else stringBuilder.append(byteToBinary(bytes[i], false));
}
System.out.println("对byte数组解码过后:");
Map<String,Character> DecodeMap=new HashMap<String,Character>();
for(char ch:huffmanTreeMapCode.keySet())
DecodeMap.put(huffmanTreeMapCode.get(ch),ch);
StringBuilder s =new StringBuilder();
for(int i=0;i<stringBuilder.length();i++)
{ boolean flag =true;
int count=1;//计步器
String b= "null";
while(flag)
{
String key = stringBuilder.substring(i, i + count);
b = String.valueOf(DecodeMap.get(key));
if(b=="null")
{
count++;
}else {
flag=false;
}
}
s.append(b);
i=i+count-1;
}
return s.toString();
}
//第六步 byte转二进制的工具类
public static String byteToBinary(byte b,boolean flag)
{
int temp=b; //将byte转为int
temp|=256;//将temp或上256 256的二进制码为1 0000 0000
String str =Integer.toBinaryString(temp);
if(flag) return str.substring(str.length()-8);
else return str.substring(str.length()-last);
}
//第五步 根据哈夫曼编码表进行编码
public static byte[] zip(Map<Character,String> huffmanTreeMapCode,String s)
{ char []arr=s.toCharArray();
StringBuilder stringBuilder =new StringBuilder();
for(char ch:arr)//遍历char数组
{
stringBuilder.append(huffmanTreeMapCode.get(ch));
}
System.out.println("哈夫曼编码过后:");
System.out.println(stringBuilder.toString());
int len =stringBuilder.length()%8==0?stringBuilder.length()/8:stringBuilder.length()/8+1;
byte [] bytes =new byte[len];
String str = stringBuilder.toString();
int index=0;//计步器
for(int i=0;i<stringBuilder.length();i+=8)
{
if((i+8)<stringBuilder.length())
bytes[index++]=(byte)Integer.parseInt(str.substring(i,i+8),2);
else
bytes[index++]=(byte)Integer.parseInt(str.substring(i),2);
}
return bytes;
}
//第四步得到哈夫曼编码表 路径值 左路径为0 右路径为1
public static void getCode(Node root ,String code ,StringBuilder stringBuilder)
{
StringBuilder stringBuilder1 =new StringBuilder(stringBuilder);
stringBuilder1.append(code);
if(root!=null)
{
if(root.english=='\0')
{
getCode(root.left,"0",stringBuilder1);
getCode(root.right,"1",stringBuilder1);
}else
{
huffmanTreeMapCode.put(root.english,stringBuilder1.toString());
}
}
}
public static List<Node> getNode(String s)
{
char[] arr = s.toCharArray();
ArrayList<Node> arrayList=new ArrayList<Node>();
Map<Character,Integer> nodesMap =new HashMap<>();
for(char c:arr)
if(!nodesMap.containsKey(c))
{
nodesMap.put(c,1);
}else nodesMap.put(c, nodesMap.get(c)+1);
for(char ch :nodesMap.keySet())
arrayList.add(new Node(nodesMap.get(ch),ch));
return arrayList;
}
//第三步得到创建哈夫曼树的根节点
public static Node CreateHuffmanTree(List<Node> list){
Collections.sort(list);
while(list.size()>1)
{
Node leftNode =list.get(0);
Node rightNode=list.get(1);
Node parentNode=new Node(leftNode.weight+rightNode.weight,'\0');
parentNode.left=leftNode;
parentNode.right=rightNode;
list.remove(leftNode);
list.remove(rightNode);
list.add(parentNode);
Collections.sort(list);
}
return list.get(0);
}
public static void preOrder(Node root)
{
System.out.println(root);
if(root.left!=null)
preOrder(root.left);
if(root.right!=null)
preOrder(root.right);
}
}
//第一步创建结点类
class Node implements Comparable<Node> {
int weight;//权值
char english;//字符
Node left;//左孩子
Node right;//右孩子
public Node(int weight, char english)
{
this.weight=weight;
this.english=english;
}
@Override
public String toString() {
return "Node{" +
" 字符=" + english +
",权重=" + weight +
'}' ;
}
@Override
public int compareTo(Node o) {
return this.weight-o.weight;
}//自定义排序
}