如果这样看麻烦的话 点击我主页面 资源那 有这个哈夫曼编码代码的文件免费下载 import java.io.*; import java.util.*;
class 哈夫曼编码 { 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()]; //创建一个byte数组大小为fis数据的长度 fis.read(bytes); //将数据读取到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()];//创建一个byte数组大小为fis数据的长度 fis.read(bytes);//将数据读取到bytes数组 fis.close();//关闭输入输出流 List<Node> list =getNode(new String(bytes)); //得到bytes数组对应的Node集合 Node root =CreateHuffmanTree(list); //得到哈夫曼树的根结点 getCode(root,"",stringBuilder);//得到huffman编码表 存入到了 开头的static HashMap<Character,String> huffmanTreeMapCode =new HashMap<>(); byte[] b=zip(huffmanTreeMapCode,new String(bytes)); // 将byte数组经过哈夫曼编码后的编码再次转为byte数组 System.out.println("huffman编码转为byte"); for(byte bb:b) { System.out.print(bb+" "); } System.out.println(); 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(); *///判断最后一个byte数组的二进制位数是多少 for(int i=0;i<bytes.length;i++) { //遍历byte数组 将其转化为二进制 拼接成字符串 if(i!=bytes.length-1) stringBuilder.append(byteToBinary(bytes[i], true)); else stringBuilder.append(byteToBinary(bytes[i], false)); } System.out.println("解码byte转为二进制:"); System.out.println(stringBuilder); Map<String,Character> DecodeMap=new HashMap<String,Character>(); //创建一个字典 for(char ch:huffmanTreeMapCode.keySet())//对huffman编码表进行遍历 DecodeMap.put(huffmanTreeMapCode.get(ch),ch);//将huffman编码表的key作为键值存入新的字典 //键值作为key存入新的字典 StringBuilder s =new StringBuilder(); for(int i=0;i<stringBuilder.length();i++) //对二进制拼接而成的字符串进行解码 //用新的字典 { boolean flag =true; //标记 默认为true int count=1;//计步器 String b= "null";//设为空 while(flag) { String key = stringBuilder.substring(i, i + count); //从字符串第0个开始取 取到第i+count个,作为key b = String.valueOf(DecodeMap.get(key));//如果在字典中找到key 则返回的不是null ,没找到就返回null if(b=="null")//如果没找到 { count++; //就让count++ }else { //如果找到 就结束循环 flag=false; } } s.append(b);//然后将其添加到字符串拼接类中 i=i+count-1;//然后i跳转到i+count-1 为什么减1 因为i++需要抵消掉 } System.out.println(s.toString()); return s.toString(); //返回解码之后的字符串 } //第六步 byte转二进制的工具类 public static String byteToBinary(byte b,boolean flag)//byte转二进制 { int temp=b; //将byte转为int temp|=256;//将temp或上256 256的二进制码为1 0000 0000 String str =Integer.toBinaryString(temp);//int转二进制 if(flag) return str.substring(str.length()-8); //如果他不是最后一个数 就正常8位取 else return str.substring(str.length()-last); //如果他是最后一个数 就先判断最后一个数的二进制位数是多少 然后再添加 } //第五步 根据哈夫曼编码表进行编码 public static byte[] zip(Map<Character,String> huffmanTreeMapCode,String s)//压缩 得到的是一个byte数组 { char []arr=s.toCharArray();//字符串转化为char数组 StringBuilder stringBuilder =new StringBuilder(); //方便字符串拼接 for(char ch:arr)//遍历char数组 { stringBuilder.append(huffmanTreeMapCode.get(ch));//哈夫曼编码的key就是ch 键值就是哈夫曼编码表的编码 } System.out.println("哈夫曼编码过后:"); System.out.println(stringBuilder.toString());//此时得到的是哈夫曼编码 但其长度很长 将其转为byte int len =stringBuilder.length()%8==0?stringBuilder.length()/8:stringBuilder.length()/8+1; //byte 每8位一个 所以数组长度就是除8 如果有剩余就加1 byte [] bytes =new byte[len];//压缩的byte数组 last=stringBuilder.length()%8; String str = stringBuilder.toString();//将字符串拼接类转为字符串 int index=0;//计步器 for(int i=0;i<stringBuilder.length();i+=8) //i每次+8代表每次字符串每次截取8位 { if((i+8)<stringBuilder.length())//如果i+8小于字符串的长度 bytes[index++]=(byte)Integer.parseInt(str.substring(i,i+8),2);//就讲字符串以二进制的方式转化为int 再强转为byte else//如果大于字符串的长度 bytes[index++]=(byte)Integer.parseInt(str.substring(i),2);//就只要i及i之后的字符串以二进制的方式转化为int 再强转为byte } return bytes; //返回byte数组 } //第四步得到哈夫曼编码表 路径值 左路径为0 右路径为1 public static void getCode(Node root ,String code ,StringBuilder stringBuilder)//传入根结点,一个路径值,一个StringBuilder { StringBuilder stringBuilder1 =new StringBuilder(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());//将其存入一个字典中 其key为char,键值为当前叶子结点的字符串拼接的路径值 } } } //第二步得到存有node结点的集合 public static List<Node> getNode(String s)//计算一句话中的每个字母出现次数并将赋值到node里并装入到集合中 { char[] arr = s.toCharArray(); //将字符串转化为字符数组 ArrayList<Node> arrayList=new ArrayList<Node>();//创建一个存 Node类型的集合 Map<Character,Integer> nodesMap =new HashMap<>();//创建一个 key 为char,键值为int的字典 for(char c:arr)//增强for遍历 字符数组 if(!nodesMap.containsKey(c))//判断这个字典的key值是否有字符c { //如果没有 就加入这个字符 并且指定键值为1 nodesMap.put(c,1); }else nodesMap.put(c, nodesMap.get(c)+1);//如果有 则让键值加1 for(char ch :nodesMap.keySet())//遍历字典的key arrayList.add(new Node(nodesMap.get(ch),ch));//加入结点 结点的权值就是 map的键值,字符就是字典的key值 showListNode(arrayList); 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;//父母的左孩子为leftnode parentNode.right=rightNode;//父母的右孩子为rightnode list.remove(leftNode);//删除第一小的结点 list.remove(rightNode);//删除第二小的结点 list.add(parentNode);//加入父亲结点 Collections.sort(list);//再次排序 } if(list.size()==0) { try { throw new RuntimeException(); }catch (Exception e) { System.out.println("根节点为空,请在被压缩文件中添加内容"); } } 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); } public static void showListNode(List<Node> list) { for(Node node:list) { System.out.println(node); } } } //第一步创建结点类 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; }//自定义排序 }
原文件数据长度8001
压缩后文件数据长度3396
还原文件数据长度8001