通过java来实现文本压缩需要以下几个步骤:
1.创建二叉树
2.根据给定的字符串内容(读取文本文件),统计每个字符出现的频率,创建对应的节点
3.根据节点的权值创建哈夫曼树,统计字符编码
4.把字符数据替换成对应的编码
5.把字符编码没每8个一组转成byte写入文件
6.把码表写入文件
7.添加图形化界面
二.根据给定的字符串内容(读取文本文件),统计每个字符出现的频率,创建对应的节点
//读取文本数据
public String readFile(String path){
System.out.println("readFile_读取文件");
File file =new File(path);//将文件封装为File类对象
FileReader fr;
String dataStr="";
try{
fr=new FileReader(file);//创建FileReader对象
BufferedReader br =new BufferedReader(fr);
String s="";
while((s=br.readLine())!=null){
dataStr+=s;
}
}catch (Exception e){
throw new RuntimeException(e);
}
return dataStr;
}
首先通过BufferedReader读取文件内容,将文件中的字符保存到dataStr中返回
3.根据节点的权值创建哈夫曼树,统计字符编码
//统计字符频率
public void number(String dataStr){
System.out.println("统计字符频率");
hm=new HashMap<>();
for (int i = 0; i < dataStr.length(); i++) {
if (!hm.containsKey(dataStr.charAt(i)+"")){//判断该key值是否在表中存在
//添加当前字符到哈希表中
hm.put(dataStr.charAt(i)+"",1);
} else{
hm.put(dataStr.charAt(i)+"",hm.get(dataStr.charAt(i)+"")+1);
}
}
//根据字符数据创建结点
for(String s:hm.keySet()){
TreeNode node =new TreeNode(hm.get(s),s);
listNode.add(node);
}
}
通过for循环在dataStr中遍历数组,统计每个字符出现的频率,因为哈希表中是<String,Integer>类型的,dataStr.charAt(i)是每个字符,所以要将char字符类型转换为String字符串类型,才能存储到哈希表中。
4.把字符数据替换成对应的编码
//统计叶子节点的哈夫曼编码
public void setCode(TreeNode root){
if(root.left != null) {
root.left.code = root.code + "0";
setCode(root.left);
}
if(root.right != null) {
root.right.code = root.code + "1";
setCode(root.right);
}
}
给二叉树上的节点创建相应编码,向左为0,向右为1;
//打印叶子节点
public void inorder(TreeNode root){
if(root!=null){
inorder(root.left);
//判断是否是叶子结点
if (root.left==null&&root.right==null){
System.out.println(root.c+" data= "+root.data+" code= "+root.code);//data字符出现频率 code字符编码
hashMap.put(root.c,root.code);
}
inorder(root.right);//向后遍历数据字符
}
}
按照中序遍历打印叶子结点,并经将字符和编码存入到哈希表当中,
//把字符数据替换成对应编码
public String setCoding(String dataStr){
System.out.println("字符替换编码");
//把字符编码转成byte写入文件
StringBuilder stringBuilder=new StringBuilder();
for (int i=0;i<dataStr.length();i++){
stringBuilder.append(codingMap.get(dataStr.charAt(i)+""));//取出码表中字符对应编码
}
return stringBuilder.toString();//转化为String类型,返回编码
}
将字符数据替换成相应编码,通过取出表中对应字符来返回编码
5.把字符编码没每8个一组转成byte写入文件
//把字符的编码数据转成byte写入文件
public void writeData(String codingStr) {
System.out.println("将字符编码转成byte写入文件");
try {
FileOutputStream fos = new FileOutputStream(("File\\compress.txt"));
int dataNum = codingStr.length()/8;//合成字节数
int last =codingStr.length()%8;//不足8位,剩余要补足的数
int len=8;
int data=0;
for (int j = 0; j < dataNum; j++) {
for (int i = 0; i < len; i++) {
//从j*len开始的len*(j+1))个字符
data=Integer.parseInt(codingStr.substring(j*len,len*(j+1)),2);
}
fos.write(data);
fos.flush();
}
String s1="";
for (int i = 0; i < 8 - last; i++) {
s1+="0";
}
String lastStr=codingStr.substring(dataNum*8,dataNum*8+last)+s1;
byte ls=(byte)Integer.parseInt(lastStr,2);
fos.write(ls);
//最后补位
fos.write(8-last);
fos.flush();
System.out.println("写文件成功!");
} catch (Exception e) {
e.printStackTrace();//指出异常的类型、性质、栈层次及出现在程序中的位置。
}
}
将字符编码写入到新文件当中,判断编码位数,如果编码不足8位,向后用0补全8位
最后合并方法就能够实现文本压缩功能
public void compress(String path){
String dataStr=readFile(path);//读取文件
System.out.println("dataStr:"+dataStr);
number(dataStr);
TreeNode root=createTree(listNode);
setCode(root);
inorder(root);
String dataCoding=setCoding(dataStr);
System.out.println("dataCoding:"+dataCoding);
writeData(dataCoding);
}
效果如下: