赞
踩
基于课设报告改编
碌碌无为的12月…
提示:以下是本篇文章正文内容,下面案例可供参考
设计一个哈夫曼编码/译码系统,对一个文本文件中的字符进行哈曼编码,生成编码文件(压缩文件,后缀名.cod);反过来,可将一个压缩文件译码还原为文本文件(.txt).
1.核心算法模块: 使用哈夫曼编码算法对给定的文本文件进行压缩/解压处理,要求可靠性强且效率高。
2.文件读写模块: 使用Java字节流的方式读写对应的文本文件,要求效率较高,且有较强的鲁棒性。
3.界面显示模块: 使用Java Swing类库来完成解压缩操作的可视化,界面要求简洁大方,易于使用,具有较强的交互性。
算法关键存储结构说明:
(1).哈夫曼树: 每一个叶子结点存储对应字符作为数据域,相应出现的次数作为权重. (2)StringBuilder: 用于拼接字符串.
(3)byte[]数组: 存储压缩生成的二进制编码(如010110). (4)Map<Byte,String>: 存储二进制数及其权重
(5)ArrayList: 对(4)中的Map再封装 (6)ObjectInputStream:
对象输出流,用于一次性将对象数据写入文件 (7) ObjectOutputStream: 对象输出流,一次性将对象读取入内存
通过Java IO流操作进行读写文件, 由于有处理中文的需求,这里选择了字节流读写文本, 防止字符流在操作压缩文件的时候出现不必要的乱码问题.
鉴于控制台操作的程序交互性较差,这里使用了Java的Swing类库, 能让用户自己选择文件进行解压缩, 解压之后的程序信息显示,读取解压文本,并自动打开文件所在的位置.
(1). zip(byte[] bytes,Map<Byte,String>huffmanCodes)
本程序的核心压缩算法, 由于需要压缩成一个二进制变量,参数一 bytes是从文本文件中读取来的二进制的数组, 而huffmanCodes则是我们根据哈夫曼树得到的键值对结构.
在算法中,我们的最终目的是得到一个二进制变量,根据计算机中原码,反码,补码的知识我们得到, 对于大于8位的字符,如果其总长度对8取模运算,很有可能结果不是0(即总长不是8的倍数),对于这种情况,我们需要对其补0,将其补位8位的二进制数.
最后,用Integer方法的parseInt(String,radix)将得到的字符串按每8位拆解的方式转换为二进制数,最后通过文本操作存入cod文件,同时也要存入哈夫曼编码表,否则在解压的时候就会匹配失败.
(2).unZip(Map<Byte,String>huffmanCodes,byte[]huffmanBytes)
本程序的核心解压算法,需要一个辅助的Map将键值对转置,即将读取到的二进制数按每8位与之前生成的哈夫曼编码表进行匹配,匹配到了就保存文本,没匹配到就继续循环,直到匹配成功.
(3). binaryToStr(byte b)
将传入的byte进行 |=0x100运算,即如果传入是正数,就补高位,反之运算不生效. 然后通过Integer.toBinaryString()方法将将byte类型的数据转为字符串并返回
(4). 计算压缩率:
压缩率=(压缩前字节长度-压缩后字节长度)/ 压缩前字节长度
使用NumberFormat类将数值类型的压缩率转换为字符串并返回显示到界面中.
(5). 关于二次封装操作的说明: 二次封装的面向对象开发中的常用手段,Java语言的关键字和类名普遍较长, 而很多时候形参列表是不能缩减的,这就造成了可读性的下降.方法中大量的变量定义,函数调用也让代码非常的不优雅, 因此对zip(byte[] bytes,Map<Byte,String>huffmanCodes)方法做了二次封装.
(1).compass(StringfromPath,String toPath)deCompass(String fromPath,String toPath):
ObjectInputStream, ObjectOutputStream(对象输入流与对象输出流).
这两个类的readObject()和writeObject()方法能将对象数据一次性写入到文件中.
Java的swing插件开发中,方法间区分不明显,下面分功能讲解
(1).三个按钮间的逻辑: 在这里,我们将三个按钮分别命名压缩,解压,保存. 页面初始化后,三个按钮都是可点击的,当我们点击压缩按钮时,解压按钮会随之禁用,当选中保存地址时, 压缩操作开始,文件自动压缩,完成后自动打开压缩文件所在的文件夹. 解压操作同理.
(2). 提示信息: 在提示信息处,会打印操作执行的时间,文件存储的地址.
(3). 文本显示: 将解压完成后的文本打印的在文本域中.
核心算法:
package edu.cust.cs.bean; import java.io.*; import java.text.NumberFormat; import java.util.*; public class HuffmanCode { static StringBuilder stringBuilder = new StringBuilder(); public static Map<Byte, String> huffmanCode = new HashMap<Byte, String>(); public static void main(String[] args) { } public static String getText(String path) { ArrayList<String> list = new ArrayList<String>(); StringBuilder stringBuilder = new StringBuilder(); String text = ""; try { File file = new File(path);// 创建文件对象file FileReader fileReader = new FileReader(file); // fr对象 对应f的读出 BufferedReader br = new BufferedReader(fileReader);// br是 对应fr的 读出缓冲区 String out = null; int i = 0; while ((out = br.readLine()) != null) { list.add(out);// 用一个数组全部存起来方便修改 System.out.println(out); } for (int j = 0; j < list.size(); j++) // stringBuilder.append(list.get(j)); text+=(list.get(j)+"\n"); br.close(); } catch (Exception ex) { ex.printStackTrace(); } // return new String(stringBuilder); return text; } public static String compass(String fromPath, String toPath) { // 创建输出流 OutputStream outputStream = null; ObjectOutputStream objectOutputStream = null; // 创建文件的输入流 FileInputStream inputStream = null; String zipRate = null; try { inputStream = new FileInputStream(fromPath); byte[] b = new byte[inputStream.available()]; inputStream.read(b); // 读取文件内容到二进制数组 byte[] huffmanBytes = zip(b); // 压缩成字节数组变量 outputStream = new FileOutputStream(toPath); objectOutputStream = new ObjectOutputStream(outputStream); // 向文件中写入哈夫曼编码和二进制数组 objectOutputStream.writeObject(huffmanBytes); objectOutputStream.writeObject(huffmanCode); // 计算压缩率 , 公式: rate = (压缩前的字节数组长度 - 压缩后) / 压缩前 NumberFormat numberFormat = NumberFormat.getInstance(); // 转换为字符串 numberFormat.setMinimumFractionDigits(1); // 小数点后保留2位 zipRate = numberFormat.format(100 * ((float) (b.length - huffmanBytes.length) / (float) b.length)); System.out.println(zipRate + "%"); // 输出压缩率 } catch (Exception e) { // TODO: handle exception System.out.println(e.getMessage()); } finally { try { // 关闭流 inputStream.close(); objectOutputStream.close(); outputStream.close(); } catch (Exception e) { // TODO: handle exception System.out.println(e.getMessage()); } } return (zipRate + "%"); } public static void deCompass(String fromPath, String toPath) { InputStream inputStream = null; ObjectInputStream oInputStream = null; // 使用对象输入流,将数据对象一次性写入到文件里 OutputStream outputStream = null; try { // 创建文件输入流 inputStream = new FileInputStream(fromPath); // 创建一个和 is关联的对象输入流 oInputStream = new ObjectInputStream(inputStream); // 读取byte数组 huffmanBytes byte[] huffmanBytes = (byte[]) oInputStream.readObject(); // byte[] huffmanBytes =(byte[]) inputStream.read(); // 读取赫夫曼编码表 Map<Byte, String> huffmanCodes = (Map<Byte, String>) oInputStream.readObject(); // 解码 byte[] bytes = unZip(huffmanCodes, huffmanBytes); outputStream = new FileOutputStream(toPath); outputStream.write(bytes); } catch (Exception e) { // TODO: handle exception System.out.println(e.getMessage()); } finally { try { outputStream.close(); oInputStream.close(); inputStream.close(); } catch (Exception e2) { // TODO: handle exception System.out.println(e2.getMessage()); } } } // public String getZipRate() { // // byte[] strBytes = str.getBytes(); // NumberFormat numberFormat = NumberFormat.getInstance(); // numberFormat.setMinimumFractionDigits(2); //小数点后保留3位 // System.out.println("压缩结果如下"); // byte[] zip = huffmanZip(strBytes); // System.out.println(Arrays.toString(zip)); // String zipRate = numberFormat.format(100 * ((float)(strBytes.length - zip.length) / (float) strBytes.length)); // System.out.println(zipRate+"%"); //输出压缩率 // return (zipRate+"%"); // } /** * 解压算法 * * @param huffmanCodes 编码表(各个字符对于的二进制数) * @param huffmanBytes //压缩后的二进制数 * @return */ public static byte[] unZip(Map<Byte, String> huffmanCodes, byte[] huffmanBytes) { Map<String, Byte> map = new HashMap<String, Byte>(); StringBuilder stringBuilder = new StringBuilder(); List<Byte> arr = new ArrayList<>(); for (int i = 0; i < huffmanBytes.length - 1; i++) { // 对二进制计算 byte b = huffmanBytes[i]; String str = binaryToStr(b); // str是二进制字符串 boolean isFinal = (i == huffmanBytes.length - 2); // 判断是否数组中余下的数字(%8 剩下的位数就是) if (isFinal) { byte by = huffmanBytes[huffmanBytes.length - 1]; str = str.substring(0, by); } stringBuilder.append(str); // 拼接字符串 } for (Map.Entry<Byte, String> entry : huffmanCodes.entrySet()) { map.put(entry.getValue(), entry.getKey()); // 键值对转置 } for (int i = 0; i < stringBuilder.length();) { int count = 1; boolean flag = true; Byte by = null; while (flag) { String key = stringBuilder.substring(i, i + count); by = map.get(key); if (by == null) { // 没匹配到完整的字符 count++; } else { // 找到了 flag = false; } } arr.add(by); i += count; } byte[] b = new byte[arr.size()]; for (int i = 0; i < b.length; i++) { // 取出二进制数组并返回 b[i] = arr.get(i); } return b; } private static String binaryToStr(byte b) { // 使用临时变量保存 b int tmp = b; tmp |= 0x100; // byte对256进行|操作,正数补高位 String str = Integer.toBinaryString(tmp); // 包装类方法,将int转为2进制的字符串 return str.substring(str.length() - 8); // } private static Node createHuffmanTree(List<Node> nodes) { while (nodes.size() > 1) { Collections.sort(nodes); Node leftNode = nodes.get(0); Node rightNode = nodes.get(1); Node parent = new Node(null, leftNode.weight + rightNode.weight); parent.left = leftNode; parent.right = rightNode; nodes.remove(leftNode); nodes.remove(rightNode); nodes.add(parent); } return nodes.get(0); } // 二次封装zip方法 public static byte[] zip(byte[] bytes) { List<Node> nodes = getNodes(bytes); Node huffmanTreeRoot = createHuffmanTree(nodes); Map<Byte, String> huffmanCodes = getCodes(huffmanTreeRoot); byte[] huffmanMapByte = zip(bytes, huffmanCodes); return huffmanMapByte; } // private static byte[] zip(byte[] bytes, Map<Byte, String> huffmanCodes) { // 压缩 int length; StringBuilder stringBuilder = new StringBuilder(); for (byte b : bytes) { stringBuilder.append(huffmanCodes.get(b)); } // 统计返回 byte[] huffmanCodeBytes 长度 byte count = (byte) (stringBuilder.length() % 8); if (count == 0) { length = stringBuilder.length() / 8; } else { length = stringBuilder.length() / 8 + 1; // 后面补零 for (int i = count; i < 8; i++) { stringBuilder.append('0'); // 补成8位二进制 } } byte[] huffmanCodeBytes = new byte[length + 1]; // byte[] huffmanCodeBytes = new byte[length]; huffmanCodeBytes[length] = count; int index = 0;// 记录是第几个byte for (int i = 0; i < stringBuilder.length(); i += 8) { String strByte; strByte = stringBuilder.substring(i, i + 8); // 将strByte 转成一个byte,放入到 huffmanCodeBytes huffmanCodeBytes[index] = (byte) Integer.parseInt(strByte, 2); index++; } return huffmanCodeBytes; } private static Map<Byte, String> getCodes(Node root) { if (root == null) { return null; } // 处理root的左子树 getCodes(root.left, "0", new StringBuilder()); // 处理root的右子树 getCodes(root.right, "1", new StringBuilder()); return huffmanCode; } // 获取结点的权重 private static List<Node> getNodes(byte[] bytes) { ArrayList<Node> nodes = new ArrayList<Node>(); Map<Byte, Integer> counts = new HashMap<Byte, Integer>(); for (byte b : bytes) { Integer count = counts.get(b); if (count == null) { // 第一次出现 counts.put(b, 1); } else { counts.put(b, count + 1); } } for (Map.Entry<Byte, Integer> entry : counts.entrySet()) { nodes.add(new Node(entry.getKey(), entry.getValue())); } return nodes; } public static void getCodes(Node node, String code, StringBuilder stringBuilder) { StringBuilder stringBuilder1 = new StringBuilder(stringBuilder); stringBuilder1.append(code); // 拼接code if (node != null) { if (node.data == null) { // 非叶子结点,就继续递归调用 getCodes(node.left, "0", stringBuilder1); getCodes(node.right, "1", stringBuilder1); } else { // 叶子结点 huffmanCode.put(node.data, stringBuilder1.toString()); // 以键值对方式加入编码表 } } } // 获取哈夫曼编码表 public static Map<Byte, String> getHuffmanCodes(byte[] bytes) { List<Node> nodes = HuffmanCode.getNodes(bytes); Node root = HuffmanCode.createHuffmanTree(nodes); huffmanCode = HuffmanCode.getCodes(root); return huffmanCode; } } class Node implements Comparable<Node> { Byte data; // 数据域 Integer weight; // 权重 Node left; Node right; public Node(Byte data, Integer weight) { this.data = data; this.weight = weight; } @Override public String toString() { return "bean.Node{" + "data=" + data + ", weight=" + weight + '}'; } @Override public int compareTo(Node o) { return this.weight - o.weight; } public void preOrder() { // 前序遍历 System.out.println(this); if (this.left != null) { this.left.preOrder(); } if (this.right != null) { this.right.preOrder(); } } }
//封装
package edu.cust.cs.service; import edu.cust.cs.bean.HuffmanCode; public class FileService { public static void main(String[] args) { } //压缩文件 public static String compress(String fromPath,String toPath) { try { String rate = HuffmanCode.compass(fromPath, toPath); return rate; }catch(Exception e) { e.printStackTrace(); return "fail"; } } //解压文件 public static String deCompress(String fromPath,String toPath) { try { HuffmanCode.deCompass(fromPath, toPath); return "解压成功"; }catch(Exception e) { e.printStackTrace(); return "fail"; } } }
界面:
package edu.cust.cs.view; import java.awt.BorderLayout; import java.awt.Desktop; import java.awt.EventQueue; import java.awt.Font; import javax.swing.JFrame; import java.awt.GridLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.JTextArea; import javax.swing.ScrollPaneConstants; import javax.swing.filechooser.FileNameExtensionFilter; import edu.cust.cs.bean.HuffmanCode; import edu.cust.cs.service.FileService; import javax.swing.JButton; import javax.swing.JFileChooser; import javax.swing.JLabel; import javax.swing.JOptionPane; import javax.swing.JScrollPane; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.io.File; import java.io.IOException; import java.text.SimpleDateFormat; import java.util.Date; public class XiaoZip { private JFrame frame; String frompath; String toPath; boolean isZip = true; //默认为压缩 String saveAsName; //保存的路径 /** * Launch the application. */ public static void main(String[] args) { EventQueue.invokeLater(new Runnable() { public void run() { try { XiaoZip window = new XiaoZip(); window.frame.setVisible(true); } catch (Exception e) { e.printStackTrace(); } } }); } /** * Create the application. */ public XiaoZip() { initialize(); } /** * Initialize the contents of the frame. */ private void initialize() { frame = new JFrame(); frame.setBounds(100, 100, 900, 750); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.getContentPane().setLayout(null); JButton btnNewButton = new JButton("\u538B\u7F29"); JButton btnNewButton_1 = new JButton("\u9009\u4E2D\u5B58\u653E\u76EE\u5F55"); btnNewButton_1.setEnabled(false); JButton btnNewButton_2 = new JButton("\u89E3\u538B"); JTextArea textArea = new JTextArea(); textArea.setBounds(14, 13, 427, 524); JTextArea textArea_1 = new JTextArea(); textArea_1.setBounds(661, 323, 207, 337); frame.getContentPane().add(textArea_1); btnNewButton_2.addMouseListener(new MouseAdapter() { //解压 @Override public void mouseClicked(MouseEvent e) { btnNewButton.setEnabled(false); //禁用压缩按钮 JFileChooser chooser = new JFileChooser(); // 设置选择器 FileNameExtensionFilter filter = new FileNameExtensionFilter("压缩包文件(.cod)", "cod"); chooser.setMultiSelectionEnabled(true); // 设为多选 chooser.setFileFilter(filter); int returnVal = chooser.showOpenDialog(btnNewButton_2); // 是否打开文件选择框 System.out.println("returnVal=" + returnVal); if (returnVal == JFileChooser.APPROVE_OPTION) { // 如果符合文件类型 System.out.println("~选择上传文件位置"); frompath = chooser.getSelectedFile().getAbsolutePath(); // 获取绝对路径 // FileUtils.compress(frompath, toPath); if(!frompath.contains("cod")) { JOptionPane.showMessageDialog(null, "文件类型错误", "提示", JOptionPane.ERROR_MESSAGE); } System.out.println("选择的文件路径为 "+frompath); System.out.println("You chose to open this file: " + chooser.getSelectedFile().getName()); // 输出相对路径 JOptionPane.showMessageDialog(null, "请选择解压文件保存路径 ", "提示", JOptionPane.INFORMATION_MESSAGE); btnNewButton_1.setEnabled(true); //取消禁用 String tmp = chooser.getSelectedFile().getName().trim(); String[] strs = tmp.split("\\."); saveAsName = strs[0]; isZip = false; //解压 System.out.println(saveAsName); //显示文件名 } } }); btnNewButton_2.setBounds(675, 103, 147, 27); btnNewButton.addMouseListener(new MouseAdapter() { @Override public void mouseClicked(MouseEvent arg0) { btnNewButton_2.setEnabled(false); //禁用解压按钮 JFileChooser chooser = new JFileChooser(); // 设置选择器 FileNameExtensionFilter filter = new FileNameExtensionFilter("文本文件", "txt"); chooser.setMultiSelectionEnabled(true); // 设为多选 chooser.setFileFilter(filter); int returnVal = chooser.showOpenDialog(btnNewButton); // 是否打开文件选择框 System.out.println("returnVal=" + returnVal); //设置为接收txt文件 if (returnVal == JFileChooser.APPROVE_OPTION) { // 如果符合文件类型 System.out.println("~选择上传文件位置"); frompath = chooser.getSelectedFile().getAbsolutePath(); // 获取绝对路径 System.out.println("选择的文件路径为 "+frompath); System.out.println("You chose to open this file: " + chooser.getSelectedFile().getName()); // 输出相对路径 JOptionPane.showMessageDialog(null, "请选择压缩文件保存路径 ", "提示", JOptionPane.INFORMATION_MESSAGE); btnNewButton_1.setEnabled(true); //取消禁用 String tmp = chooser.getSelectedFile().getName().trim(); String[] strs = tmp.split("\\."); //不加\\无法截取 saveAsName = strs[0]; System.out.println(saveAsName); } } }); btnNewButton_1.addMouseListener(new MouseAdapter() { @Override public void mouseClicked(MouseEvent e) { // 按钮点击事件 System.out.println("~选择压缩文件位置"); JFileChooser chooser = new JFileChooser(); // 设置选择器 // chooser.setMultiSelectionEnabled(true); // 设为多选 chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); int returnVal = chooser.showOpenDialog(btnNewButton_1); // 是否打开文件选择框 System.out.println("returnVal=" + returnVal); if (returnVal == JFileChooser.APPROVE_OPTION) { // 如果符合文件类型 if(chooser.getSelectedFile().isDirectory()) { //选择的是文件夹就保存 toPath = chooser.getSelectedFile().getAbsolutePath(); String tmp = toPath; if(isZip) { toPath += ("/"+saveAsName)+".cod"; System.out.println(toPath); //压缩率 String rate = FileService.compress(frompath, toPath); textArea_1.setFont(new Font("黑体",Font.BOLD,16)); SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");//设置日期格式 textArea_1.append(df.format(new Date())+"\n"); textArea_1.append("压缩率 "+rate); textArea_1.append("\n文件存放地址为 "+toPath); try { Desktop.getDesktop().open(new File(tmp)); //打开所在目录 } catch (IOException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } btnNewButton_2.setEnabled(true); } else { textArea.setEnabled(true); textArea.setText(" "); toPath += ("/"+saveAsName)+".txt"; System.out.println(toPath); FileService.deCompress(frompath, toPath); //获取当前时间 SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");//设置日期格式 // System.out.println(df.format(new Date()));// new Date()为获取当前系统时间 textArea_1.setFont(new Font("黑体",Font.BOLD,16)); textArea_1.append(("\n"+df.format(new Date())+"\n")); textArea_1.append("解压完成,请查收文件"); textArea_1.append("\n"+"文件存放地址为 "+toPath); textArea.setText( HuffmanCode.getText(toPath)); textArea.setEnabled(false); btnNewButton_2.setEnabled(true);; try { Desktop.getDesktop().open(new File(tmp)); //打开所在目录 } catch (IOException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } btnNewButton_1.setEnabled(true); } } } } }); btnNewButton_1.setBounds(675, 166, 147, 27); btnNewButton.setBounds(675, 30, 147, 27); frame.getContentPane().add(btnNewButton); frame.getContentPane().add(btnNewButton_1); JLabel lblNewLabel = new JLabel("\u63A7\u5236\u53F0"); lblNewLabel.setBounds(727, 323, 72, 18); frame.getContentPane().add(lblNewLabel); // frame.getContentPane().add(textArea); JScrollPane scrollPane = new JScrollPane(textArea); scrollPane.setBounds(13, 10, 634, 650); scrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED); scrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED); JScrollPane scrollPane_1 = new JScrollPane(textArea_1); scrollPane_1.setBounds(661, 354, 207, 306); scrollPane_1.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED); scrollPane_1.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED); frame.getContentPane().add(scrollPane, BorderLayout.PAGE_START); frame.getContentPane().add(scrollPane_1, BorderLayout.PAGE_END); frame.getContentPane().add(btnNewButton_2); } }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。