赞
踩
通过实验掌握从文件中读取数据及将运行结果保存到文件中的方法,了解Huffman编码的算法设计及使用Java实现的方法。
对文件Input.txt中的字符使用Huffman编码进行编码,将编码结果保存到文件Output.txt文件中,最后对Output.txt文件中的字符进行译码。
程序要先统计文件中字符的种类数,每种字符的个数,然后通过Huffman算法计算出各字符的Huffman编码,使用该编码对文件进行编码,并将结果保存到Output.txt文件中。最后对Output.txt文件中的字符进行译码,将译码结果保存到文件Output1.txt中。
1.文件Output.txt大小应比文件Input.txt文件的小。
2.译码结果应和原文件input.txt中的内容一致。
先读取文件,创建一个256大小的数组b保存字符出现的频率,下标对应该字符的ASCII码,值为该字符出现的频率,通过循环遍历读取文件内容,统计各个字符出现的频率;创建一个HashMap来保存该文件字符及其对应的频率,key值为字符的ASCII码,value值为频率,通过遍历数组b,如果数组元素不等0,则将该元素的下标存到HashMap的key值,对应的value为该元素的值,最后返回该HashMap。
public static HashMap<Integer, Integer> getCharWeight(String inputFile) throws IOException { FileInputStream inputStream = new FileInputStream(inputFile); HashMap<Integer, Integer> hashMap = new HashMap<>(); int[] b = new int[256]; int read; while ((read=inputStream.read())!=-1){ b[read]+=1; } for (int i = 0; i < b.length; i++) { if (b[i]!=0){ hashMap.put(i,b[i]); } } return hashMap; }
该节点类的属性包含权重(weight)、ASCII码(asc)、树的左孩子(lchild)和树的右孩子(rchild),一个全参构造方法。
class Node{
int weight; //权重
int asc; //该的字符ACll码
Node lchild; //左孩子
Node rchild; //右孩子
//构造方法
public Node(int weight, int asc, Node lchild, Node rchild) {
this.weight = weight;
this.asc = asc;
this.lchild = lchild;
this.rchild = rchild;
}
}
先创建一个ArrayList来保存叶子结点,将上面已保存的每个字符及其对应的频率添加到ArrayList当中;对列表中的元素根据结点的权重来按小到大进行排序,取出排序后列表中的权重最小和次小的结点,将该两个结点的权重相加的值作为该两个结点的父结点,左孩子为最小权重的结点,右孩子为次小权重的结点,将该父结点加入列表中,并移除最小和次小的结点;重复以上该过程,直到列表中只剩一个结点时,则退出循环;最后,返回该结点,即为huffman树的根结点。
public static Node createTree(HashMap<Integer, Integer> charWeight){ //创建保存叶子结点的列表 ArrayList<Node> tree = new ArrayList<>(); charWeight.forEach((k,v)->{ tree.add(new Node(v,k,null,null)); }); //构造Huffman树 while (tree.size()!=1){ //当列表中只剩下一个结点的时候则退出循环 //根据树的权重进行排序 Collections.sort(tree, Comparator.comparingInt(o -> o.weight)); //将排序后的列表中取前面两个最小的树的结点进行合并 //权重为该两个结点的权重和,左孩子为最小权重的结点,右孩子为次小权重的结点 //重新构造一颗树结点,将该结点加到原来的列表当中 //然后,将最小的两个结点从列表中移除 tree.add(new Node(tree.get(0).weight+tree.get(1).weight,-1,tree.get(0),tree.get(1))); tree.remove(tree.get(0)); tree.remove(tree.get(0)); } //返回列表中的第一个结点,即为该Huffman树的根结点 return tree.get(0); }
先遍历huffman树的根结点,再遍历树的左孩子,每遍历一个左孩子结点,在code字符串后加“0”,直到左孩子为null时,则返回继续遍历右孩子,每遍历一个右孩子结点,在code字符串后加“1”,右孩子为null时,则huffman树遍历完成。最后,将各个字符ASCII码及其对应的huffman编码存到HashMap当中。(huffmanCode和huffmanCode2为全局HashMap变量,其中huffmanCode的key为huffman编码,value为ASCII码,huffmanCode2的key为ASCII码,value为huffman编码)
public static void printHuffmanCode(Node root, String code) {
if(root!=null) {
if(root.lchild==null&&root.rchild==null) {
huffmanCode.put(code,root.asc);
huffmanCode2.put(root.asc,code);
}
printHuffmanCode(root.lchild,code+"0");
printHuffmanCode(root.rchild,code+"1");
}
}
先读取文件,通过循环遍历文件中的每一个字符,寻找该字符的ASCII码在huffmanCode2中的key值所对应的huffman编码,将该huffman编码加到编码字符串中,当编码字符串的长度大于等于31的时候,截取该编码字符串的前31位01字符串转为十进制,将转为后的十进制数字写入文件中,再删除编码字符串前31位的01字符串,如果最后一段01字符串不足31位,则将剩下的部分转为十进制。最后,将编码后的字符串写入文件中。
public static void coding(String inputFile, String outputFile) throwsIOException { //编码文件 FileInputStream inputStream = new FileInputStream(inputFile); //保存编码后的文件 DataOutputStream outputStream = new DataOutputStream(new FileOutputStream(outputFile)); int read; //编码后的字符串 StringBuilder codingContent = new StringBuilder(); int n=31;//01字符串转为十进制的位数 int num;//十进制数 while ((read=inputStream.read())!=-1){ String s = *huffmanCode2*.get(read);//获取该ASCII码对应的huffman编码 codingContent.append(s);//将huffman加入到codingContent中 if (codingContent.length()>=n){ num = Integer.*valueOf*(codingContent.substring(0,n),2);// 截取31位01字符串转为十进制 outputStream.writeInt(num);//将十进制数字写入文件 codingContent.delete(0,n);//删除已经转为十进制的01字符串 } } //处理最后一个不足31位的01字符串 num = Integer.*valueOf*(codingContent.substring(0,codingContent.length()),2); outputStream.writeInt(num); *lastNum* = num;//最后一个不足31位的01字符串转为十进制的数字 *lastLen* = codingContent.length();//最后一个不足31位的01字符串的长度 outputStream.close(); }
先读取解码文件,读取文件中的十进制数字,将十进制转为二进制,如果转换后的二进制位数不等于31位,则在该二进制前面添加0,直到二进制位数等于31位为止,对于最后一个二进制数要与前面最后保存的01字符串的长度进行比较,若两者不等则在该二进制前面添加0,直到两者长度相等为止。最后,将转换后的二进制01字符串保存到编码字符串中,通过循环遍历编码字符串中的每一个字符,每次循环将该字符加入临时字符串中,如果huffmanCode的key包含该临时字符串,则将该临时字符串所对应的字符进行解码,将解码后的字符加入到解码后的字符串中,接着删除临时字符串,将它置为空,重复进行上述操作,直到遍历结束。最后,将解码后的字符串写入文件中。
public static void decoding(String inputFile, String outputFile) throws IOException { DataInputStream dis = new DataInputStream(new FileInputStream(inputFile)); int a;//文件读取的十进制数字 int l;//十进制数字转为二进制后的长度与31位的差值 StringBuilder coding = new StringBuilder();//编码字符串 StringBuilder s;// 保存二进制的字符串 while (dis.available()>0){ a = dis.readInt(); s = new StringBuilder(Integer.*toBinaryString*(a));//将读取的十进制数转为二进制 //若十进制数字转为二进制后的长度不等于31,则在该二进制数前面补充l个0 if (a==*lastNum*){//处理最后一个二进制数 l = *lastLen*-s.length(); for (int i = 0; i < l; i++) { s.insert(0,"0"); } }else if ((l=31-s.length())!=0){ for (int i = 0; i < l; i++) { s.insert(0,"0"); } } coding.append(s);//将二进制字符串加入到编码字符串中 } //临时字符串 StringBuilder str = new StringBuilder(); //解码后的字符串 StringBuilder decodingContent = new StringBuilder(); for (int i=0;i<coding.length();i++) { str.append(coding.charAt(i));//将字符加入临时字符串中 //如果临时字符串等于已存在的huffman编码,则将该huffman编码解码为对应的字符 if (*huffmanCode*.containsKey(str.toString())) { String s1 = String.*valueOf*((char) *huffmanCode*.get(str.toString()).intValue());//将ASCII码转为char,再将char转为String decodingContent.append(s1);//将解码后的字符加到decodingContent字符串中 str.delete(0, str.length());//删除临时字符串 } } //解码后的文件 FileOutputStream outputStream = new FileOutputStream(outputFile); //将解码后的字符串写入到文件中 outputStream.write(decodingContent.toString().getBytes()); outputStream.close(); }
字符 | ASCII | 频率 | Huffman编码 | 字符 | ASCII | 频率 | Huffman编码 |
---|---|---|---|---|---|---|---|
r | 114 | 13 | 0000 | ; | 59 | 4 | 110000 |
p | 112 | 3 | 000100 | = | 61 | 2 | 1100010 |
: | 58 | 1 | 0001010 | } | 125 | 2 | 1100011 |
{ | 123 | 2 | 0001011 | I | 73 | 1 | 11001000 |
\n | 10 | 7 | 00011 | [ | 91 | 1 | 11001001 |
\r | 13 | 7 | 00100 | ] | 93 | 1 | 11001010 |
s | 115 | 7 | 00101 | f | 102 | 1 | 11001011 |
y | 121 | 7 | 00110 | B | 66 | 5 | 110011 |
g | 103 | 8 | 00111 | b | 98 | 10 | 11010 |
e | 101 | 16 | 0100 | S | 83 | 5 | 110110 |
( | 40 | 8 | 01010 | . | 46 | 5 | 110111 |
) | 41 | 8 | 01011 | h | 104 | 1 | 11100000 |
a | 97 | 4 | 011000 | w | 119 | 1 | 11100001 |
c | 99 | 4 | 011001 | d | 100 | 3 | 1110001 |
l | 108 | 4 | 011010 | o | 111 | 6 | 111001 |
u | 117 | 4 | 011011 | i | 105 | 12 | 11101 |
n | 110 | 17 | 0111 | t | 116 | 24 | 1111 |
空格 | 32 | 66 | 10 |
目标文件的大小为270字节,Huffman编码后的文件为152字节,文件压缩率43.7%
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。