4.1 哈夫曼编/译码器
4.1.1 项目简介
哈夫曼编/译码器问题:利用哈夫曼编码(加密)进行信息通讯可以大大提高信道利用率,缩短信息传输时间,降低传输成本,保证报文安全。这要求在发送端通过一个编码系统对待传数据预先编码;在接收端将传来的数据进行译码(解密)。设计要求如下:
一个完整的系统应具有以下功能:
(1)初始化 (Initialization):从终端读入n个字符,建立哈夫曼树;
(2)编码 (Coding):利用已建好的哈夫曼树,对字符进行编码,然后将正文编码结果存入文件codefile中;
(3)译码 (Decoding):利用已建好的哈夫曼树将文件codefile中的代码进行译码,结果存入文件textfile中。
建议:
- 将本次实验与《计算机网络》或者《计算机组成原理》中的通讯协议相结合,对报文进行加密\解密。
代码清单
MainJFrame
package ex4_Huffman;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
/**
* @Version 1.0
* @Author:刘昕
* @Date:2021/3/30 11:06
* @Software: IntelliJ IDEA
* @Content:
*/
public class MainJFrame extends JFrame implements ActionListener {
public static Label text;
JPanel functionPanel, textPanel, loadPanel, encodePanel, decodePanel, plainTextPanel, encodeTextPanel, decodeTextPanel;
static JTextArea plainText;
static JTextArea encodeText;
static JTextArea decodeText;
JScrollPane plainTextScroll, encodeTextScroll, decodeTextScroll;
JButton loadButton, encodeButton, decodeButton;
JTextField path;
JLabel pathLabel;
//存储明文
static String plain_text = "";
public MainJFrame(){
super("哈夫曼编码/译码");
this.setLayout(new BorderLayout());
this.setSize(900, 600);
this.setLocationRelativeTo(null);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//添加功能面板
functionPanel = new BackgroundPanel();
functionPanel.setBorder(BorderFactory.createTitledBorder("功能区"));
loadPanel = new JPanel();
encodePanel = new JPanel();
decodePanel = new JPanel();
loadButton = new JButton("加载");
loadButton.addActionListener(this);
encodeButton = new JButton("编码");
encodeButton.addActionListener(this);
decodeButton = new JButton("译码");
decodeButton.addActionListener(this);
pathLabel = new JLabel("路径:");
path = new JTextField(26);
this.getContentPane().add(BorderLayout.NORTH, functionPanel);
functionPanel.add(loadPanel);
functionPanel.add(encodePanel);
functionPanel.add(decodePanel);
loadPanel.add(pathLabel);
loadPanel.add(path);
loadPanel.add(loadButton);
encodePanel.add(encodeButton);
decodePanel.add(decodeButton);
//添加文本面板
textPanel = new JPanel(new GridLayout(1,3));
textPanel.setBorder(BorderFactory.createTitledBorder("文本区"));
textPanel.setBackground(Color.GRAY);
this.getContentPane().add(BorderLayout.CENTER, textPanel);
plainTextPanel = new BackgroundPanel();
plainTextPanel.setBorder(BorderFactory.createTitledBorder("明文"));
textPanel.add(plainTextPanel);
encodeTextPanel = new BackgroundPanel();
encodeTextPanel.setBorder(BorderFactory.createTitledBorder("编码"));
textPanel.add(encodeTextPanel);
decodeTextPanel = new BackgroundPanel();
decodeTextPanel.setBorder(BorderFactory.createTitledBorder("译文"));
textPanel.add(decodeTextPanel);
plainText = new JTextArea(57,29);
plainText.setBackground(Color.pink);
plainText.setLineWrap(true);
plainText.setFont(new Font("楷体",Font.BOLD,16));
encodeText = new JTextArea(57,29);
encodeText.setBackground(Color.pink);
encodeText.setLineWrap(true);
encodeText.setFont(new Font("楷体",Font.BOLD,16));
decodeText = new JTextArea(57,29);
decodeText.setBackground(Color.pink);
decodeText.setLineWrap(true);
decodeText.setFont(new Font("楷体",Font.BOLD,16));
plainTextScroll = new JScrollPane(plainText);
plainTextScroll.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
encodeTextScroll = new JScrollPane(encodeText);
encodeTextScroll.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
decodeTextScroll = new JScrollPane(decodeText);
decodeTextScroll.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
plainTextPanel.add(plainTextScroll);
encodeTextPanel.add(encodeTextScroll);
decodeTextPanel.add(decodeTextScroll);
this.setVisible(true);
}
@Override
public void actionPerformed(ActionEvent e) {
//加载明文按键
if (e.getSource() == loadButton){
String Path = path.getText();
ReadFile.readTextFile(Path);
}
//输出编码按键
else if (e.getSource() == encodeButton){
new Huffman();
Huffman.encodeFunc();
}
//输出译码按键
else if (e.getSource() == decodeButton){
Huffman.decodeFunc();
}
}
public static void main(String[] args){
new MainJFrame();
}
}
Node
package ex4_Huffman;
/**
* @Version 1.0
* @Author:刘昕
* @Date:2021/3/30 10:55
* @Software: IntelliJ IDEA
* @Content: class Node
*/
public class Node {
String ch;
int num;
Node leftChild = null;
Node rightChild = null;
public Node(){}
public Node(String ch, int num) {
this.ch = ch;
this.num = num;
}
@Override
public String toString() {
return "Node{" +
"ch='" + ch + '\'' +
", num=" + num +
'}';
}
public String getCh() {
return ch;
}
public void setCh(String ch) {
this.ch = ch;
}
public int getNum() {
return num;
}
public void setNum(int num) {
this.num = num;
}
public Node getLeftChild() {
return leftChild;
}
public void setLeftChild(Node leftChild) {
this.leftChild = leftChild;
}
public Node getRightChild() {
return rightChild;
}
public void setRightChild(Node rightChild) {
this.rightChild = rightChild;
}
public int compareTo(Node o2){
return this.getNum() - o2.getNum();
}
}
Huffman
package ex4_Huffman;
import ex1_学生报考系统.Student;
import java.util.*;
/**
* @Version 1.0
* @Author:刘昕
* @Date:2021/3/30 10:59
* @Software: IntelliJ IDEA
* @Content:
*/
public class Huffman {
//字符及出现频率放入哈希表中
HashMap<String, Integer> map = new HashMap<>();
//编码
static HashMap<String, String> encodeMap = new HashMap<>();
//译码
static HashMap<String, String> decodeMap = new HashMap<>();
//哈希表中的数据放入数组中, 方便排序建树
ArrayList<Node> array = new ArrayList<>();
static Node tree;
public Huffman(){
//把字符放入到哈希表中, key代表字符, value是出现频率
for (int i = 0; i < MainJFrame.plain_text.length(); i++){
String ch = MainJFrame.plain_text.charAt(i) + "";
//这个字符在map中没有,放入map
if (map.get(ch) == null){
map.put(ch, 1);
}
//这个字符在map中存在,value+1重新放入
else{
map.replace(ch, map.get(ch), map.get(ch)+1 );
}
}
//哈希表中的数据放入数组中
for (Map.Entry<String, Integer> t : map.entrySet()){
Node temp = new Node(t.getKey(), t.getValue());
array.add(temp);
}
//进行排序
sortFunc();
//n为叶子节点数目, n2为2度节点数目
int n = array.size();
int n2 = n - 1;
//建立哈夫曼树
for (int j = 0; j < n2; j++){
Node t = new Node(null, array.get(0).getNum()+array.get(1).getNum());
t.leftChild = array.get(0);
t.rightChild = array.get(1);
array.add(t);
array.remove(0);
array.remove(0);
sortFunc();
}
tree = array.get(0);
}
public void sortFunc(){
Collections.sort(array, new Comparator<Node>() {
@Override
public int compare(Node o1, Node o2) {
return o1.compareTo(o2);
}
});
}
//编码函数
public static void encodeFunc(){
encodeFunc2(Huffman.tree, "", encodeMap, decodeMap);
for (int i = 0; i < MainJFrame.plain_text.length(); i++){
MainJFrame.encodeText.append(encodeMap.get(MainJFrame.plain_text.charAt(i)+""));
}
}
public static void encodeFunc2(Node tree, String code, HashMap<String, String> encodeMap, HashMap<String, String> decodeMap){
if (tree.rightChild == null ){
//编码表
encodeMap.put(tree.ch, code);
//译码表
decodeMap.put(code, tree.ch);
}
else{
encodeFunc2(tree.leftChild, code + "0", encodeMap, decodeMap);
encodeFunc2(tree.rightChild, code + "1", encodeMap, decodeMap);
}
}
//译码函数
public static void decodeFunc(){
String s_decodeFunc = MainJFrame.encodeText.getText();
int j = 0;
for (int i = 0; i < s_decodeFunc.length(); ){
j++;
String s_decodeFunc2 = s_decodeFunc.substring(i, j);
if (decodeMap.get(s_decodeFunc2) != null){
MainJFrame.decodeText.append(decodeMap.get(s_decodeFunc2));
i = j;
}
}
}
}
BackGroundPanel
package ex4_Huffman;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.event.ActionEvent;
import javax.swing.ImageIcon;
import javax.swing.JPanel;
/**
* @Version 1.0
* @Author:刘昕
* @Date:2021/3/30 11:55
* @Software: IntelliJ IDEA
* @Content:
*/
public class BackgroundPanel extends JPanel {
ImageIcon icon;
Image img;
public BackgroundPanel() {
// /img/HomeImg.jpg 是存放在你正在编写的项目的bin文件夹下的img文件夹下的一个图片
icon = new ImageIcon(getClass().getResource("./Imag/3.jpg"));
img = icon.getImage();
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
//下面这行是为了背景图片可以跟随窗口自行调整大小,可以自己设置成固定大小
g.drawImage(img, 0, 0, this.getWidth(), this.getHeight(), this);
}
}
ReadFile
package ex4_Huffman;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStreamReader;
/**
* @Version 1.0
* @Author:刘昕
* @Date:2021/3/30 14:28
* @Software: IntelliJ IDEA
* @Content:
*/
public class ReadFile {
public static void readTextFile(String path){
String encoding = "utf-8";
try{
File file = new File(path);
if (file.isFile() && file.exists()){
InputStreamReader read = new InputStreamReader(new FileInputStream(file),encoding);
BufferedReader bufferedReader = new BufferedReader(read);
String lineTxt = null;
while((lineTxt = bufferedReader.readLine()) != null){
MainJFrame.plainText.append(lineTxt);
MainJFrame.plain_text += lineTxt;
}
read.close();
}
else{
System.out.println("文件不存在!");
}
}
catch(Exception e){
System.out.println("读取文件出错");
e.printStackTrace();
}
}
}
源代码链接:
链接:https://pan.baidu.com/s/166JJ1cdQ2fqWoYg_BqtVGA
提取码:Code