目录
一、问题描述
利用哈夫曼编码进行信息通讯可以大大提高信道利用率,缩短信息传输时间,降低传输成本。但是,这要求在发送端通过一个编码系统对待传数据预先编码;在接收端将传来的数据进行译码(复原)。对于双工信道 (即可以双向传输信息的信道),每端都需要一个完整的编/译码系统。试对于任意的一段文本(可能是直接输入的,也可能是保存在本地文件中或者网络上的),写一个哈夫曼码的编译码系统。
二、基本要求
一个完整的系统应具有以下功能:
(l)I:初始化 (Initialization)。从终端读入字符集大小 n,及 n 个字符和 m 个权值,建立哈夫曼树,并将它存于文件 hfmtree 中。
(2)C:编码 (Coding)。利用已建好的哈夫曼树(如不在内存,则从文件 hfmtree 中读入),对文件 tobetrans 中的正文进行编码,然后将结果存入文件 codefile 中。
(3)D:解码(Decoding)。利用已建好的哈夫曼树将文件 codefile 中的代码进行译码,结果存入文件 textfile 中。
(4)P:打印代码文件 (Print)。将文件 codefile 以紧凑格式显示在终端上,每行 50 个代码。同时将此字符形式的编码文件写入文件 codeprint 中。
(5)T:打印哈夫曼树 (Tree printing)。将已在内存中的哈夫曼树以直观的方式 (树或凹入表形式)显示在终端上,同时将此字符形式的哈夫曼树写入文件 treeprint 中。
三、逻辑结构设计
Huffman编码过程中会根据字符出现的概率高低来构造一棵树,存在一对多的关系,直观呈现“层次”特征,故Huffman编码用树形结构。
四、存储结构设计
Huffman树结构上有左右子结点的关系,故可以让一个存储结点包含其子树的邻接关系,也就是用链式存储结构。
图Huffman UML
五、主要操作设计
1.编码:当Huffman树tree初始化之后,tree调用coding()方法,获取单个字符与编码的关系,然后tree调用encoding()方法,与文件中的所有字符进行比对,完成输入内容的编码。
2. 解码:map中存放着字符与编码的关系,将传进来的字符串从头到尾一个一个地与map中的数据进行比对,发现相同,就将其输入到textfile中。
六、技术难点与解决方法
难点:实现过程中哈夫曼树的打印用JavaFX比较困难。
解决方法:在createPrintTree方法,传入相关参数,node是该哈夫曼树的root结点,row是X轴的偏移量,col是Y轴的偏移量,deep是树的深度。进入方法之后,根据坐标的计算,分别将对应的结点放在相应的位置。然后递归调用,先后处理父节点的左右孩子。
七、实现与展示
八、详细代码
import java.io.*;
import java.util.*;
public class Coding {
private Map<String,Object> codes;
//当Huffman树tree初始化之后,tree调用coding()方法,获取单个字符与编码的关系,然后tree调用encoding()方法,与文件中的所有字符进行比对,完成输入内容的编码
public Coding(){
codes=new HashMap<>();
}
public void buildHuffmanCodes() throws Exception{
HuffmanTreeNode Htree=readTree();
String buildCode="";
code(Htree,buildCode,codes);
}
private void code(HuffmanTreeNode Htree,String buildCode,Map<String ,Object> codes){
if (Htree!=null){
if (Htree.getLeftChild()==null && Htree.getRightChild()==null){
codes.put(buildCode,Htree.getData());
}
code(Htree.getLeftChild(),buildCode+"0",codes);
code(Htree.getRightChild(),buildCode+"1",codes);
}
}
private HuffmanTreeNode readTree() throws Exception{
ObjectInputStream ois=new ObjectInputStream(new FileInputStream("hfmtree.txt"));
try {
return (HuffmanTreeNode) ois.readObject();
} catch (IOException e){
System.out.println("读取文件失败!");
return null;
}
}
public void save() throws Exception{
DataOutputStream os=new DataOutputStream(new FileOutputStream("codefile.txt"));
List<Object> text=readText();
Set<String > keys=codes.keySet();
try {
for (int i=0;i<text.size();i++){
for (String key: keys){
if (text.get(i).equals(codes.get(key))) {
os.writeUTF(key);
break;
}
}
}
os.close();
System.out.println("编码成功!已保存至文件(codefile.txt)!");
} catch (IOException e){
System.out.println("保存失败!");
}
}
private List<Object> readText() throws Exception{
DataInputStream is=new DataInputStream(new FileInputStream("tobetrans.txt"));
List<Object> text=new ArrayList<>();
try{
while (true){
text.add(is.readChar());
}
} catch (EOFException e){}
return text;
}
public Map<String, Object> getCodes() {
return codes;
}
public void setCodes(Map<String, Object> codes) {
this.codes = codes;
}
}
package experimentFinal2;
import java.io.*;
import java.util.*;
public class Decoding {
private Map<String,Object> codes;//map中存放着字符与编码的关系,将传进来的字符串从头到尾一个一个地与map中的数据进行比对,发现相同,就将其输入到textfile中。
public Decoding() throws Exception{
codes=createMap();
}
public Map<String,Object> createMap() throws Exception{
codes=new HashMap<>();
List<String> code=readCode();
for (int i=0;i<code.size();i++) {
codes.put(code.get(i),null);
}
return codes;
}
public void DecodingHuffmanTree() throws Exception{
HuffmanTreeNode Htree=readTree();
String buildCode="";
decode(Htree,buildCode,codes);
}
private void decode(HuffmanTreeNode Htree,String buildCode,Map<String ,Object> codes){
if (Htree!=null){
if (Htree.getLeftChild()==null && Htree.getRightChild()==null){
codes.put(buildCode,Htree.getData());
}
decode(Htree.getLeftChild(),buildCode+"0",codes);
decode(Htree.getRightChild(),buildCode+"1",codes);
}
}
private HuffmanTreeNode readTree() throws Exception{
ObjectInputStream ois=new ObjectInputStream(new FileInputStream("hfmtree.txt"));
try {
return (HuffmanTreeNode) ois.readObject();
} catch (IOException e){
System.out.println("读取文件失败!");
return null;
}
}
public List<String> readCode() throws Exception{
DataInputStream is=new DataInputStream(new FileInputStream("codefile.txt"));
List<String> codes=new ArrayList<>();
try {
while (true){
codes.add(is.readUTF());
}
} catch (EOFException e){}
return codes;
}
public void save() throws Exception{
DataOutputStream oos=new DataOutputStream(new FileOutputStream("textfile.txt"));
List<String> code=readCode();
Set<String> keys=codes.keySet();
try {
for (int i=0;i<code.size();i++){
for (String key: keys){
if (code.get(i).equals(key)) {
oos.writeChar((char)codes.get(key));
break;
}
}
}
oos.close();
System.out.println("解码成功!已保存至文件(textfile.txt)!");
} catch (IOException e){
System.out.println("保存失败!");
}
}
public Map<String, Object> getCodes() {
return codes;
}
public void setCodes(Map<String, Object> codes) {
this.codes = codes;
}
}
package experimentFinal2;
import java.io.*;
import java.util.ArrayList;
import java.util.List;
public class HuffmanTree {
private HuffmanTreeNode Htree;
private int leafNode=0;
private int depth=0;
public HuffmanTree(){
Htree=new HuffmanTreeNode();
}
public void createHuffmanTree() throws Exception{
List<HuffmanTreeNode> leafNodes=readFromlFile();
this.leafNode=leafNodes.size();
if (leafNode==0){
return;
}
while (leafNodes.size()>1){
sort(leafNodes);//排序
HuffmanTreeNode LChild=leafNodes.get(0);//左孩子为0
HuffmanTreeNode RChild=leafNodes.get(1);//右孩子为1
HuffmanTreeNode parent=new HuffmanTreeNode(null,LChild.getWeight()+RChild.getWeight());
//双亲的权值合为左孩子、右孩子权值的合
parent.setLeftChild(LChild);
parent.setRightChild(RChild);
leafNodes.remove(0);
leafNodes.remove(0);
leafNodes.add(parent);
}
Htree=leafNodes.get(0);
}
public void creatNew(String s) throws Exception {
DataOutputStream os=new DataOutputStream(new FileOutputStream("tobetrans.txt"));
for (int i=0;i<s.length();i++){
os.writeChar(s.charAt(i));
}
os.close();
}
private List<HuffmanTreeNode> readFromlFile() throws Exception{
DataInputStream is=new DataInputStream(new FileInputStream("tobetrans.txt"));
List<HuffmanTreeNode> leafNodes=new ArrayList<>();
HuffmanTreeNode p;
try {
while (true) {
//统计字符在文档中出现的概率 概率的大小作为权值
p=new HuffmanTreeNode();
char data=is.readChar();
int index=weighting(leafNodes,data);
if (index!=-1){
leafNodes.get(index).setWeight(leafNodes.get(index).getWeight()+1);
} else {
p.setData(data);
p.setWeight(1);
leafNodes.add(p);
}
}
} catch (EOFException e){}
is.close();
return leafNodes;
}
public void print() throws Exception{
List<HuffmanTreeNode> leafNodes=readFromlFile();
System.out.println("文件字符集:");
for (HuffmanTreeNode node: leafNodes){
System.out.println("字符:"+node.getData()+" 权值:"+node.getWeight());
}
System.out.println();
}
private int weighting( List<HuffmanTreeNode> leafNodes,char c){
//找到相应的叶子结点并返回下标
for (int i=0;i<leafNodes.size();i++){
if (leafNodes.get(i).getData().equals(c)){
return i;
}
}
return -1;
}
private void sort(List<HuffmanTreeNode> leafNodes){
//按权值排序
for (int i=0;i<leafNodes.size();i++){
for (int j=0;j<leafNodes.size()-1-i;j++){
if (leafNodes.get(j).getWeight()>leafNodes.get(j+1).getWeight()){
HuffmanTreeNode p=leafNodes.get(j);
leafNodes.set(j,leafNodes.get(j+1));
leafNodes.set(j+1,p);
}
}
}
}
public void save() throws Exception{
ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream("hfmtree.txt"));
try{
oos.writeObject(Htree);
oos.close();
System.out.println("初始化成功!哈夫曼树已创建!");
} catch (IOException e){
System.out.println(e.getMessage());
}
}
public void deepCount(HuffmanTreeNode Htree,int count){
//求深度
if (Htree!=null){
count++;
if (Htree.getLeftChild()==null && Htree.getRightChild()==null){
//如果没有孩子
if (count>this.depth){
this.depth=count;
}
}
deepCount(Htree.getLeftChild(),count);
deepCount(Htree.getRightChild(),count);
}
}
public HuffmanTreeNode getHtree() {
return Htree;
}
public void setHtree(HuffmanTreeNode htree) {
Htree = htree;
}
public int getLeafNode() {
return leafNode;
}
public void setLeafNode(int leafNode) {
this.leafNode = leafNode;
}
public int getDepth() {
int count=0;
deepCount(this.Htree,count);
return depth;
}
public void setDepth(int depth) {
this.depth = depth;
}
}
package experimentFinal2;
import java.io.Serializable;
public class HuffmanTreeNode implements Serializable {
private Object data;
private int weight=0;
private HuffmanTreeNode leftChild=null;
private HuffmanTreeNode rightChild=null;
private int parent=-1;
public HuffmanTreeNode(){
}
public HuffmanTreeNode(Object data,int weight){
this(data,weight,null,null,-1);
}
public HuffmanTreeNode(Object data,int weight,HuffmanTreeNode leftChild,HuffmanTreeNode rightChild, int parent){
this.data=data;
this.weight=weight;//权值
this.leftChild=leftChild;//左孩子
this.rightChild=rightChild;//右孩子
this.parent=parent;//双亲
}
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
public int getWeight() {
return weight;
}
public void setWeight(int weight) {
this.weight = weight;
}
public HuffmanTreeNode getLeftChild() {
return leftChild;
}
public void setLeftChild(HuffmanTreeNode leftChild) {
this.leftChild = leftChild;
}
public HuffmanTreeNode getRightChild() {
return rightChild;
}
public void setRightChild(HuffmanTreeNode rightChild) {
this.rightChild = rightChild;
}
public int getParent() {
return parent;
}
public void setParent(int parent) {
this.parent = parent;
}
}
package experimentFinal2;
import java.io.*;
public class Print {
private StringBuilder code;
public Print() throws Exception{
code=readCode();
}
public void printCodefile(){
System.out.println("codefile.txt:");
int begin=0;
int end=50;
for (;begin<code.length();begin=begin+50,end=end+50){
if (end>code.length()){
end=code.length();
}
System.out.println(code.substring(begin,end));
}
}
private StringBuilder readCode() throws Exception{
DataInputStream is=new DataInputStream(new FileInputStream("codefile.txt"));
StringBuilder codes=new StringBuilder();
try {
while (true){
codes.append(is.readUTF());
}
} catch (EOFException e){}
return codes;
}
public void save() throws Exception{
DataOutputStream os=new DataOutputStream(new FileOutputStream("codeprint.txt"));
try {
os.writeUTF(code.toString());
os.close();
System.out.println("代码已写入文件(codeprint.txt)!");
} catch (IOException e){
System.out.println("保存失败!");
}
}
}
package experimentFinal2;
import java.util.Scanner;
public class Test {
public static void main(String[] args) throws Exception{
System.out.println("数据压缩与解压缩:");
int choice;
boolean running=true;
while (running){
choice=menu();
switch (choice){
case 1: initialization();break;
case 2: coding();break;
case 3: decoding();break;
case 4: print();break;
case 5: treePrint();break;
case 6: running=false;
}
}
System.out.println("谢谢使用");
}
public static int menu(){
while (true){
System.out.println("-----------------------------------");
System.out.println("请选择功能: ");
System.out.println(" 1. I:初始化(Initialization) ");
System.out.println(" 2. C:编码(Coding) ");
System.out.println(" 3. D:解码(Decoding) ");
System.out.println(" 4. P:打印代码文件(Print) ");
System.out.println(" 5. T:打印哈夫曼树(Tree printing)");
System.out.println(" 6. 退出 ");
System.out.println("-----------------------------------");
System.out.print("请输入您的选择:");
Scanner input =new Scanner(System.in);
int choice=input.nextInt();
if (choice>0 && choice<7){
return choice;
} else {
System.out.println("该选项不存在,请重新输入!\n");
}
}
}
public static void initialization() throws Exception{
Scanner input=new Scanner(System.in);
HuffmanTree Htree=new HuffmanTree();
System.out.println("--------------------------");
System.out.println("1.新建字符集 2.读取文件");
System.out.println("--------------------------");
System.out.print("请输入您的选择:");
int choice;
while (true){
choice=input.nextInt();
if (choice==1 || choice==2){
break;
}
System.out.println("选择错误,重新选择!");
}
if (choice==1){
System.out.print("新字符集:");
String s=input.next();
Htree.creatNew(s);
}
Htree.print();
Htree.createHuffmanTree();
Htree.save();
}
//当Huffman树tree初始化之后,tree调用coding()方法,获取单个字符与编码的关系
public static void coding() throws Exception{
Coding coding=new Coding();
coding.buildHuffmanCodes();
coding.save();
}
public static void decoding() throws Exception{
Decoding decoding=new Decoding();
decoding.DecodingHuffmanTree();
decoding.save();
}
public static void print() throws Exception{
Print print=new Print();
print.printCodefile();
print.save();
}
public static void treePrint() throws Exception{
TreePrinting treePrinting=new TreePrinting();
TreePrinting.main();
treePrinting.save();
}
}
package experimentFinal2;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.*;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Line;
import javafx.scene.text.Text;
import javafx.stage.Stage;
import java.io.DataOutputStream;
import java.io.FileOutputStream;
public class TreePrinting extends Application {
private String codes="";
public static void main(){
Application.launch();
}
public BorderPane pane() throws Exception{
HuffmanTree Htree=new HuffmanTree();
Pane pane=new Pane();
GridPane gridPane=new GridPane();
gridPane.setHgap(20);//水平间距
gridPane.setVgap(20);//垂直间距
BorderPane borderPane=new BorderPane();
Text text=new Text("Enter a text");
TextField textField=new TextField();
textField.setMinWidth(800);
Button button=new Button("Show Huffman Tree");
button.setOnAction(e->{
try {
pane.getChildren().clear();
Htree.creatNew(textField.getText());
Htree.createHuffmanTree();
Htree.save();
Coding coding=new Coding();
coding.buildHuffmanCodes();
coding.save();
Decoding decoding=new Decoding();
String s=textField.getText()+" is encoded to ";
for (String str: decoding.readCode()){
this.codes+=str;
}
s+=codes;
if (textField.getText() .equals("")){
s="please enter a text !";
}
createTree(Htree.getHtree(),Htree.getDepth()*50,pane,50,400);
Alert alert = new Alert(Alert.AlertType.INFORMATION);
alert.titleProperty().set("Encode Text to Bits");
alert.headerTextProperty().set(s);
alert.showAndWait();
} catch (Exception ex) {
ex.printStackTrace();
}
});
FlowPane flowPane=new FlowPane();
flowPane.getChildren().addAll(text,textField,button);
borderPane.setTop(flowPane);
Text text1=new Text("Enter a bit string");
TextField textField1=new TextField();
textField1.setMinWidth(800);
Button button1=new Button("Decode Text");
button1.setMinWidth(100);
button1.setOnAction(e->{
try {
Htree.createHuffmanTree();
String codes=textField1.getText();
StringBuilder s=new StringBuilder(textField1.getText()+" is encoded to ");
decode(codes,s,Htree.getHtree());
if (textField1.getText().equals("")){
s.replace(0,s.length(),"please enter a string !");
}
Alert alert = new Alert(Alert.AlertType.INFORMATION);
alert.titleProperty().set("Decode Bits to Text");
alert.headerTextProperty().set(s.toString());
alert.showAndWait();
} catch (Exception ex) {
ex.printStackTrace();
}
});
FlowPane flowPane1=new FlowPane();
flowPane1.getChildren().addAll(text1,textField1,button1);
borderPane.setBottom(flowPane1);
borderPane.setCenter(pane);
return borderPane;
}
public void start(Stage primaryStage) throws Exception{
Scene scene=new Scene(pane(),1000,800);
primaryStage.setTitle("HuffmanTree");
primaryStage.setScene(scene);
primaryStage.show();
}
public void decode(String codes,StringBuilder s,HuffmanTreeNode Htree){
for (int i=0;i<codes.length();i++) {
HuffmanTreeNode p=Htree;
for (;i<codes.length();i++) {
if (codes.charAt(i) == '1') {
p=p.getRightChild();
} else {
p=p.getLeftChild();
}
if(p.getLeftChild()==null && p.getRightChild()==null){
s.append(p.getData());
break;
}
}
}
}
public void createTree(HuffmanTreeNode Htree,int depth,Pane pane,int row,int col){
if (Htree!=null) {
Circle circle = new Circle(20);
circle.setFill(null);
circle.setStroke(Color.BLACK);
Label label = new Label(Htree.getWeight() + "", circle);
label.setContentDisplay(ContentDisplay.CENTER);
label.setLayoutX(col);
label.setLayoutY(row);
pane.getChildren().add(label);
row+=50;col-=depth;
int row1=row,col1=2*depth+col;
depth/=2;
if(Htree.getLeftChild()!=null){
Line line=new Line(label.getLayoutX(),label.getLayoutY()+30,col+35,row+5);
Text text1=new Text("0");
text1.setX((line.getStartX()+line.getEndX())/2-5);
text1.setY((line.getStartY()+line.getEndY())/2-5);
pane.getChildren().addAll(line,text1);
}
if (Htree.getRightChild()!=null){
Line line2=new Line(label.getLayoutX()+40,label.getLayoutY()+30,col1+5,row1+5);
Text text2=new Text("1");
text2.setX((line2.getStartX()+line2.getEndX())/2-5);
text2.setY((line2.getStartY()+line2.getEndY())/2-5);
pane.getChildren().addAll(line2,text2);
}
if(Htree.getLeftChild()==null && Htree.getRightChild()==null){
Text text=new Text(Htree.getData()+"");
text.setX(label.getLayoutX()+20);
text.setY(label.getLayoutY()+60);
pane.getChildren().add(text);
}
createTree(Htree.getLeftChild(),depth, pane,row,col);
createTree(Htree.getRightChild(),depth, pane,row1,col1);
}
}
public void save() throws Exception{
DataOutputStream os=new DataOutputStream(new FileOutputStream("TreePrint.txt"));
os.writeUTF(this.codes);
os.close();
}
}