java 32位压缩_文件的压缩与解压(java实现)

压缩与解压文件

题目要求

实现一个基于哈夫曼树的文件压缩程序和文件解压程序。

基本要求:

(1) 要求压缩程序读入源文件,分析每种字符的频度,然后建立相应的哈夫曼树,再求出相应哈夫曼编码,根据编码对源文件进行压缩,得到源文件对应的压缩文件。

(2) 解压程序读入压缩文件,根据相应的哈夫曼编码解压还原,得到对应的源文件。

(3) 求出压缩率;

需求分析

1) 创建一个文件输入流,并把它和指定目录下的文件建立联系。

2) 判断有多少个字节可以读取,并创建一个字节数组,再将每个字节的个数进行统计存入map集合,将其作为创建哈夫曼树的权重值。

3) 遍历map集合,用字节和个数逐个创建Node节点,并将Node节点存入一个list里面。

4) 遍历list集合创建哈夫曼树,返回哈夫曼树的根节点。

5) 利用根节点递归遍历获取每个字节的哈夫曼编码,再将哈夫曼编码转为十进制数存到一个字节数组之中。

6) 创建一个对象输出流(ObjectOutputStream)和文件输出流,用文件输出流与指定目录下的zip文件建立联系,再用对象输出流将上面求出来的字节数组和map集合输出到文件输出流方便之后用对象读入流来读取(重构)对象。

7) 利用对象读入流(ObjectInputStream)取出之前存入的对象,即:字节数组和map集合。

8) 利用byteToBitString函数将字节数组中的十进制数转为二进制即哈夫曼编码。

9) 将所有字节的哈夫曼编码拼接在一起存入一个字符串中,从第一位开始逐位读取,并判断map集合中是否有对应的字节,如果有将其存入一个list集合中。

10) 所有字节读取完毕之后,将list转存在一个字节数组中并返回。用文件输出流将其写入文件。

思路分析

题目的要求基于哈夫曼树的原理,那么首先要做的就是创建一个哈夫曼树,从而求出对应字节的哈夫曼编码。在此之前要统计出文件里面有多少个字节可以读取,相同字节的个数有多少,将其作为创建哈夫曼树时候的权重值,这样遍历哈夫曼树就可以得到整个文件的哈夫曼编码,然后每8个一位求出对应的10进制数将其存入一个字节数组之中。这时候我们得到一个map和一个字节数组zip。map之中存放的是键值对,键就是不同的字节,值便是字节对应的哈夫曼编码。字节数组中存放的是哈夫曼编码的10进制表示方式。将其存入对象字节流

ObjectOutputStream 将 Java 对象的基本数据类型和图形写入 OutputStream。可以使用 ObjectInputStream 读取(重构)对象。通过在流中使用文件可以实现对象的持久存储。如果流是网络套接字流,则可以在另一台主机上或另一个进程中重构对象。

代码实现

1 importjava.io.FileInputStream;2 importjava.io.FileOutputStream;3 importjava.io.InputStream;4 importjava.io.ObjectInputStream;5 importjava.io.ObjectOutputStream;6 importjava.io.OutputStream;7 importjava.nio.channels.FileChannel;8 importjava.text.NumberFormat;9 importjava.util.ArrayList;10 importjava.util.Collections;11 importjava.util.HashMap;12 importjava.util.List;13 importjava.util.Map;14 importjava.util.Set;15

16 public classHumanCodeTest17 {18

19 public static voidmain(String[] args)20 {21 String srcFile="e://code//123.txt";//要压缩的文件

22 String dstFile="e://code//123.zip";//压缩后的文件

23 zipFile(srcFile, dstFile);//压缩文件

24 unZipFile(dstFile,"e://code//unzip.txt");//对刚才的文件进行解压,解压后的文件名称叫做unzip.txt

25 }26

27 public static voidunZipFile(String zipFile,String dstFile)28 {29 InputStream inputStream=null;30 ObjectInputStream objectInputStream=null;31 OutputStream outputStream=null;32 try

33 {34 inputStream=new FileInputStream(zipFile); //将压缩文件读入

35 objectInputStream=new ObjectInputStream(inputStream); //对象操作流,讲一个对象读入

36 byte [] array= (byte [])objectInputStream.readObject(); //把每个字节的哈夫曼编码的对应十进制数读入

37 Map map=(Map)objectInputStream.readObject();//把每个字节对应的哈夫曼编码读入

38 byte[] decode =decode(map, array);39 outputStream=newFileOutputStream(dstFile);40 outputStream.write(decode);41 System.out.println("解压文件成功!");42 } catch(Exception e)43 {44 System.out.println(e);45 }finally

46 {47 try{48 outputStream.close();49 objectInputStream.close();50 inputStream.close();51

52 } catch(Exception e2) {53 System.out.println(e2);54 }55

56 }57

58

59 }60

61 public static voidzipFile(String srcFile,String dstFile)62 {63 FileInputStream inputStream=null;64 OutputStream outputStream=null;65 ObjectOutputStream objectOutputStream=null;66 FileInputStream zipfile = null;67 FileChannel fs=null;68 FileChannel zip=null;69 try

70 {71 inputStream=newFileInputStream(srcFile);72 byte [] b=new byte[inputStream.available()]; //获取文件的所有字节,这个方法可以在读写操作前先得知数据流里有多少个字节可以读取

73 fs =inputStream.getChannel();74 inputStream.read(b);75 byte[] huffmanZip =huffmanZip(b);76 outputStream=newFileOutputStream(dstFile);77 objectOutputStream=new ObjectOutputStream(outputStream); //对象操作流:该流可以将一个对象写出,或者读取一个对象到程序中,也就是执行了序列化和反序列化操作。

78 objectOutputStream.writeObject(huffmanZip);79 objectOutputStream.writeObject(map);80

81 zipfile = newFileInputStream(dstFile);82 zip =zipfile.getChannel();83 NumberFormat numberFormat =NumberFormat.getInstance();84 numberFormat.setMaximumFractionDigits(2);85 String result = numberFormat.format((float)zip.size()/(float)fs.size()*100);86 System.out.println("压缩率:"+result+"%");87 System.out.println("压缩成功!");88 } catch(Exception e)89 {90 System.out.println(e);91 }92 finally

93 {94 if(inputStream!=null)95 {96 try

97 {98 objectOutputStream.close();99 outputStream.close();100 inputStream.close();//释放资源

101 zipfile.close();102

103 } catch(Exception e2)104 {105 System.out.println(e2);106 }107

108 }109 }110 }111

112 private static byte[] decode(Map map,byte[] array)113 {114 StringBuilder stringBuilder = newStringBuilder();115 for(int i=0;i

119 }120

121 Map map2=new HashMap();//反向编码表

122 Set keySet =map.keySet();123 for(Byte b:keySet)124 {125 String value=map.get(b);126 map2.put(value, b);127 }128

129

130 List list=new ArrayList();131 for (int i = 0; i

145 {146 flag=false;147 }148

149 }150 list.add(byte1);151 i+=count;152 }153

154 byte [] by=new byte[list.size()];155 for(int i=0;i

162 private static String byteToBitString(boolean flag, byteb)163 {164 int temp=b;165 if(flag)166 {167 temp|=256; //与256做位运算 256的二进制形式为 11111111

168 }169

170 String binaryString = Integer.toBinaryString(temp);//他的作用是把一个10进制数转为32位的2进制数。同时对负数,会用补码表示。

171 if(flag)172 {173 return binaryString.substring(binaryString.length()-8);174 }175 else

176 {177 returnbinaryString;178 }179

180 }181

182 private static byte[] huffmanZip(byte [] array) //整个文件的字节数组

183 {184 List nodes = getNodes(array); //获取节点,内容是字节及其对应的个数

185 Node createHuffManTree = createHuffManTree(nodes); //利用获取的节点创建一个哈夫曼树

186 Map m=getCodes(createHuffManTree); //把哈夫曼的根节点传入,获取存放每个字节(key)和它对应的哈夫曼编码(value)

187 byte[] zip =zip(array, m);188 returnzip;189 }190

191 //192 private static byte[] zip(byte [] array,Map map) //压缩,将每一个字符的哈夫曼编码变为十进制数

193 {194 StringBuilder sBuilder=new StringBuilder(); //整个文件的哈夫曼编码

195 for(byte item:array) //遍历整个文件的字节数组,并且将其哈夫曼编码拼接成字符串

196 {197 String value=map.get(item);198 sBuilder.append(value);199 }200 //System.out.println(sBuilder);

201 intlen;202 if(sBuilder.toString().length()%8==0)//如果可以整除,

203 {204 len=sBuilder.toString().length()/8;205 }206 else //如果不能整除

207 {208 len=sBuilder.toString().length()/8+1;209 }210

211 byte [] by=new byte[len];212 int index=0;213 for(int i=0;isBuilder.length())217 {218 string=sBuilder.substring(i);219 }220 else

221 {222 string=sBuilder.substring(i, i+8);223 }224

225 by[index]=(byte)Integer.parseInt(string,2); //输出2进制数string在十进制下的数.

226 index++;227 }228

229

230 returnby;231

232 }233

234

235 //重载

236 private static MapgetCodes(Node root)237 {238 if(root==null)239 {240 return null;241 }242 getCodes(root.leftNode,"0",sBuilder);243 getCodes(root.rightNode,"1",sBuilder);244 returnmap;245 }246

247

248

249 //获取哈夫曼编码

250 static Map map=new HashMap<>(); //创建一个map集合,存放每个字节(key)和它对应的哈夫曼编码(value)

251 static StringBuilder sBuilder=newStringBuilder();252 public static voidgetCodes(Node node,String code,StringBuilder stringBuilder)253 {254 StringBuilder stringBuilder2=newStringBuilder(stringBuilder);255 stringBuilder2.append(code);256 if(node!=null)257 {258 if(node.data==null)//非叶子结点

259 {260 //向左递归

261 getCodes(node.leftNode,"0",stringBuilder2);262 //向右递归

263 getCodes(node.rightNode,"1",stringBuilder2);264 }265 else //如果是叶子结点

266 {267 map.put(node.data,stringBuilder2.toString());268 }269 }270 }271

272

273

274 public static List getNodes(byte[] array)275 {276 List list=new ArrayList();277 Map map=new HashMap();278 for(Byte data:array) //遍历字节数组,目的为了统计相同字节的个数

279 {280 Integer count=map.get(data);//通过键获取值

281 if(count==null)//说明此时map集合中还没有此字符

282 {283 map.put(data, 1);284 }285 else

286 {287 map.put(data,count+1);288 }289 }290 //遍历map集合

291 Set set=map.keySet(); //吧map中所有的key取出来,放到set集合中,方便一会儿构造节点

292 for(Byte key:set) //遍历set集合获取对应字节的个数,并创建一个node对象,并且把它放到list里面

293 {294 int value=map.get(key);295 Node node=newNode(key, value);296 list.add(node);297 }298 returnlist;299 }300

301 private static Node createHuffManTree(Listlist)302 {303 while(list.size()>1)304 {305 Collections.sort(list);//先对集合进行排序,排序关键字是value即字节的个数

306 Node leftNode=list.get(0);307 Node rightNode=list.get(1);308

309 Node parentNode=new Node(null, leftNode.weight+rightNode.weight);310 parentNode.leftNode=leftNode;311 parentNode.rightNode=rightNode;312

313 list.remove(leftNode);314 list.remove(rightNode);315

316 list.add(parentNode);317 }318 return list.get(0); //返回哈夫曼树的根。

319

320 }321

322 }323

324 class Node implements Comparable

325 {326 Byte data;//字符

327 int weight;//字符出现的次数

328 Node leftNode;329 Node rightNode;330

331 public Node(Byte data,int weight)//构造器

332 {333 this.data=data;334 this.weight=weight;335 }336

337 @Override338 public intcompareTo(Node o)339 {340 return this.weight-o.weight;341 }342

343 @Override344 publicString toString()345 {346 return "Node [data=" + data + ", weight=" + weight + "]";347 }348

349 //前序遍历

350 public voidpreOrder()351 {352 System.out.println(this);353 if(this.leftNode!=null)354 {355 this.leftNode.preOrder();356 }357 if(this.rightNode!=null)358 {359 this.rightNode.preOrder();360 }361 }362

363

364 }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
综合实验: 1. 问题描述 利用哈夫曼编码进行通信可以大大提高信道利用率,缩短信息传输时间,降低传输成本。这要求在发送端通过一个编码系统对待传输数据预先编码,在接收端将传来的数据进行译码(复原)。对于双工信道(即可以双向传输信息的信道),每端都需要一个完整的编/译码系统。试为这样的信息收发站编写一个哈夫曼码的编/译码系统。 2. 基本要求 一个完整的系统应具有以下功能: (1) I:初始化(Initialization)。从终端读入字符集大小n,以及n个字符和n个权值,建立哈夫曼树,并将它存于文件hfmTree中。 (2) E:编码(Encoding)。利用已建好的哈夫曼树(如不在内存,则从文件hfmTree中读入),对文件ToBeTran中的正文进行编码,然后将结果存入文件CodeFile中。 (3) D:译码(Decoding)。利用已建好的哈夫曼树将文件CodeFile中的代码进行译码,结果存入文件Textfile中。 (4) P:印代码文件(Print)。将文件CodeFile以紧凑格式显示在终端上,每行50个代码。同时将此字符形式的编码文件写入文件CodePrin中。 (5) T:印哈夫曼树(Tree printing)。将已在内存中的哈夫曼树以直观的方式(比如树)显示在终端上,同时将此字符形式的哈夫曼树写入文件TreePrint 中。 3. 测试数据 用下表给出的字符集和频度的实际统计数据建立哈夫曼树,并实现以下报文的编码和译码:“THIS PROGRAME IS MY FAVORITE”。 字符 A B C D E F G H I J K L M 频度 186 64 13 22 32 103 21 15 47 57 1 5 32 20 字符 N O P Q R S T U V W X Y Z 频度 57 63 15 1 48 51 80 23 8 18 1 16 1

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值