⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 compress.java

📁 哈夫曼编码 文件压缩 使用java语言对文件进行编码压缩
💻 JAVA
字号:
package hfmCompress;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.EOFException;
import java.io.FileNotFoundException;
import java.io.IOException;
import javax.swing.JOptionPane;

public class Compress {

	private Heap minHeap = null;

	private final int NUMBER_OF_KINDS_OF_BYTES = 256;

	private final int ADJUST_NUMBER = NUMBER_OF_KINDS_OF_BYTES / 2;

	private int[] numberOfEachByte = new int[NUMBER_OF_KINDS_OF_BYTES];// 统计各个字节的频率

	private Binary[] binaryNode = null;// 保存各个字节信息

	private int[] compressedCode = new int[NUMBER_OF_KINDS_OF_BYTES];// 压缩后的各个编码

	private int[] eachCodeLength = new int[NUMBER_OF_KINDS_OF_BYTES];// 压缩后的各个编码长度

	private boolean[] isNotEmpty = new boolean[NUMBER_OF_KINDS_OF_BYTES];// 指示每个字节出现频率是否为零

	private int numberOfNotZero = 0;// 出现频率不为零的字节数目

	private int numberOfInts = 0;

	private int numberOfLast = 0;

	private int numberOfBytes = 0;
	
	public Compress() {
		numberOfNotZero = 0;
		for (int i = 0; i < NUMBER_OF_KINDS_OF_BYTES; i++) {
			isNotEmpty[i] = false;
		}
	}

	public void readAndSort(String fileNameArg) {
		DataInputStream input=null;
		try {
			input= new DataInputStream(new FileInputStream(
					fileNameArg));
			int a = 0;
			Process.setCompressTrue(10,"正在读取原文件……");
			while (true) {
				a = input.readByte() + ADJUST_NUMBER;
				numberOfEachByte[a]++;
				if (!isNotEmpty[a]) {
					isNotEmpty[a] = true;
					numberOfNotZero++;
				}
			}
		} catch (FileNotFoundException fnfe) {
			JOptionPane.showMessageDialog(null, "读取文件时出现错误,没有找到相关文件,\n请重新运行。",
					"文件压缩", JOptionPane.ERROR_MESSAGE);
			System.exit(0);
			// 找不到文件
		} catch (EOFException eofe) {
			try{
				input.close();
			}catch(IOException ioe) {
				JOptionPane.showMessageDialog(null, "关闭文件输入流时出现错误,\n请重新运行。", "文件压缩",
						JOptionPane.ERROR_MESSAGE);
				
			}
		} catch (IOException ioe) {
			JOptionPane.showMessageDialog(null, "读取文件时出现错误,\n请重新运行。", "文件压缩",
					JOptionPane.ERROR_MESSAGE);
			System.exit(0);
		}
	}

	private void setNumberOfBytes() {
		numberOfBytes = 0;
		for (int i = 0; i < NUMBER_OF_KINDS_OF_BYTES; i++) {
			numberOfBytes += numberOfEachByte[i];
		}
	}

	public void creatMinHeap() {
		binaryNode = new Binary[numberOfNotZero];
		int numberOfBianryHasCreated = 0;
		for (int i = 0; i < NUMBER_OF_KINDS_OF_BYTES; i++) {
			if (isNotEmpty[i]) {
				binaryNode[numberOfBianryHasCreated] = new Binary();
				binaryNode[numberOfBianryHasCreated]
						.setByteToCoded((byte) (i - ADJUST_NUMBER));
				binaryNode[numberOfBianryHasCreated]
						.setNumberFound(numberOfEachByte[i]);
				numberOfBianryHasCreated++;
			}
		}
		minHeap = new Heap(binaryNode, numberOfNotZero);
	}

	private Binary creatHFM() {
		Process.setCompressTrue(15,"正在创建最小堆……");
		Binary least = null, less = null, newBinary = null;
		for (int i = 1; i < numberOfNotZero; i++) {
			least = minHeap.deleteMin();
			less = minHeap.deleteMin();
			newBinary = new Binary(least, less);
			newBinary.setNumberFound(least.getNumberFound()
					+ less.getNumberFound());
			minHeap.add(newBinary);
		}
		Binary b = minHeap.deleteMin();
		return b;
	}	
	public  void creatCode(Binary rootBinaryArg) {
		if (!rootBinaryArg.isLeaf()) {
			rootBinaryArg.addOneBitInLeft(true);// 左子树加一
			creatCode(rootBinaryArg.left);
			rootBinaryArg.addOneBitInLeft(false);// 右子树加零
			creatCode(rootBinaryArg.right);
		} else {
			compressedCode[rootBinaryArg.getByteToCoded() + ADJUST_NUMBER] = rootBinaryArg
					.getNowCode();
			eachCodeLength[rootBinaryArg.getByteToCoded() + ADJUST_NUMBER] = rootBinaryArg
					.getNowLength();		
			if (rootBinaryArg.getNowLength() > 32) {
				JOptionPane.showMessageDialog(null,
						"创建霍夫曼编码时,存在编码长度大于整数范围,\n程序存在漏洞,请与作者联系。", "文件压缩",
						JOptionPane.ERROR_MESSAGE);
				System.exit(0);
			}
		}
	}
	public void calBytesAndLastBits() {
		Process.setCompressTrue(25,"正在生成HFM编码……");
	creatCode(creatHFM());	
		for (int i = 0; i < NUMBER_OF_KINDS_OF_BYTES; i++) {
			numberOfInts += (eachCodeLength[i] * numberOfEachByte[i] + numberOfLast) / 32;
			numberOfLast = (eachCodeLength[i] * numberOfEachByte[i] + numberOfLast) % 32;
		}
	}

	// 压缩成的霍夫曼文件,待压缩文件,保存字节频率的数组
	public void writeCode(String newFolderNameArg, String newFileNameArg,
			String oldFolderNameArg, String oldFileNameArg) {
		try {
			int nowWritingInt = 0;
			int nowLeftBits = 0;
			int nowReadingInt = 0;
			int newIntLength = 0;
			byte nextByte = 0;
			Process.setCompressTrue(30,"正在计算文件大小……");
			DataOutputStream output = new DataOutputStream(
					new FileOutputStream(newFolderNameArg + newFileNameArg
							+ ".HFM"));
			output.writeUTF(oldFileNameArg);
			output.writeInt(numberOfNotZero);
			for (int i = 0; i < NUMBER_OF_KINDS_OF_BYTES; i++) {
				if (isNotEmpty[i]) {
					output.writeByte((byte) (i - ADJUST_NUMBER));
					output.writeInt(numberOfEachByte[i]);
				}
			}
			output.writeInt(numberOfInts);
			output.writeInt(numberOfLast);
			DataInputStream input = new DataInputStream(new FileInputStream(
					oldFolderNameArg + oldFileNameArg));
			nextByte = input.readByte();
			nowWritingInt = compressedCode[nextByte + ADJUST_NUMBER];
			nowLeftBits = 32 - eachCodeLength[nextByte + ADJUST_NUMBER];
			nowWritingInt <<= nowLeftBits;
			setNumberOfBytes();
			for (int i = 1; i < numberOfBytes; i++) {
				nextByte = input.readByte();
				nowReadingInt = compressedCode[nextByte + ADJUST_NUMBER];
				newIntLength = eachCodeLength[nextByte + ADJUST_NUMBER];
				if (newIntLength > nowLeftBits) {// 判断是否构成四个字节,如果是则输出并保存剩余位
					nowWritingInt |= nowReadingInt >> (newIntLength - nowLeftBits);
					output.writeInt(nowWritingInt);
					Process.setCompressTrue(30+(int)(65*i/numberOfBytes),"正在创建压缩文件……");
					nowLeftBits = 32 - (newIntLength - nowLeftBits);
					nowWritingInt = nowReadingInt << nowLeftBits;
				} else if (newIntLength < nowLeftBits) {// 如果不够四个字节,则合并现有位
					nowWritingInt |= (nowReadingInt << (nowLeftBits - newIntLength));
					nowLeftBits -= newIntLength;
				} else {// 如果恰好构成四个字节.则
					nowWritingInt |= nowReadingInt;
					output.writeInt(nowWritingInt);
					nowWritingInt = 0;
					nowLeftBits = 32;
				}
			}
			Process.setCompressTrue(95,"正在结束压缩……");
			if (nowLeftBits != 32) {
				output.writeInt(nowWritingInt);// 输出最后四个字节
			}
			input.close();
			output.close();
		} catch (FileNotFoundException fnfe) {
			JOptionPane.showMessageDialog(null, "读取文件时出现错误,没有找到相关文件,\n请重新运行。",
					"文件压缩", JOptionPane.ERROR_MESSAGE);
			System.exit(0);
		} catch (EOFException eofe) {
			JOptionPane.showMessageDialog(null, "读取文件时出现错误,超出文件大小,\n压缩文件可能已损坏,请重新运行。",
					"文件压缩", JOptionPane.ERROR_MESSAGE);
			System.exit(0);
		} catch (IOException ioe) {
			JOptionPane.showMessageDialog(null, "读取文件时出现错误,请重新运行。", "文件压缩",
					JOptionPane.ERROR_MESSAGE);
			System.exit(0);
		}
	}

	public void deCompress(String newFolderNameArg, String newFileNameArg,
			String oldFolderNameArg, String oldFileNameArg) {
		try {
			if (!oldFileNameArg.endsWith(".HFM")) {
				if (JOptionPane.showConfirmDialog(null,
						"你选择的文件可能不是由本程序创建,\n解压时可能出现异常并且可能不可用,\n是否坚持解压?",
						"文件解压", JOptionPane.YES_NO_OPTION) == JOptionPane.NO_OPTION) {
					return;
				}
			}
			DataInputStream input = new DataInputStream(new FileInputStream(
					oldFolderNameArg + oldFileNameArg));
			String newFileName = input.readUTF();
			DataOutputStream output = null;
			if (!newFileNameArg.equals(newFileName.substring(0, newFileName
					.lastIndexOf(".")))) {
				int userChooser = JOptionPane
						.showConfirmDialog(
								null,
								"你选择的文件名与原文件名不符,\n是否解压为原文件名?\n单击是解压为原文件名,\n单击否解压为选择文件名,",
								"文件压缩", JOptionPane.YES_NO_CANCEL_OPTION);
				if (userChooser == JOptionPane.YES_OPTION) {
					output = new DataOutputStream(new FileOutputStream(
							newFolderNameArg + newFileName));
				} else if (userChooser == JOptionPane.NO_OPTION) {
					output = new DataOutputStream(new FileOutputStream(
							newFolderNameArg
									+ newFileNameArg
									+ newFileName.substring(newFileName
											.lastIndexOf("."))));
				} else {
					return;
				}
			} else {
				output = new DataOutputStream(new FileOutputStream(
						newFolderNameArg + newFileNameArg));
			}
			Main.showProcess();
			Process.setCompressTrue(10,"正在获得字节数量……");
			numberOfNotZero = input.readInt();
			int nextNotEmptyOne = 0;
			for (int i = 0; i < numberOfNotZero; i++) {
				nextNotEmptyOne = input.readByte() + ADJUST_NUMBER;
				numberOfEachByte[nextNotEmptyOne] = input.readInt();
				isNotEmpty[nextNotEmptyOne] = true;
			}			
			creatMinHeap();
			Process.setCompressTrue(20,"正在创建HFM树……");
			Binary root = creatHFM();
			Binary nowBinary = root;
			numberOfInts = input.readInt();
			numberOfLast = input.readInt();
			int nowReadingInt = 0;
			Process.setCompressTrue(25,"正在解码压缩文件……");
			for (int i = 0; i < numberOfInts; i++) {
				Process.setCompressTrue(25+(int)(65*i/numberOfInts),"正在解码压缩文件……");
				nowReadingInt = input.readInt();
				for (int j = 0; j < 32; j++) {
					if (nowBinary.isLeaf()) {
						output.writeByte(nowBinary.getByteToCoded());
						nowBinary = root;
					}
					nowBinary = Binary.explainCode(nowReadingInt, 31 - j,
							nowBinary);
				}
			}
			Process.setCompressTrue(90,"正在结束解压……");
			if (numberOfLast != 0) {
				nowReadingInt = input.readInt();
				for (int j = 0; j < numberOfLast; j++) {
					if (nowBinary.isLeaf()) {
						output.writeByte(nowBinary.getByteToCoded());
						nowBinary = root;
					}
					nowBinary = Binary.explainCode(nowReadingInt, 31 - j,
							nowBinary);
				}
				if (nowBinary.isLeaf()) {
					output.writeByte(nowBinary.getByteToCoded());// 将最后一个字符输出
				} else {
					JOptionPane.showMessageDialog(null,
							"解压文件时出现未知错误,最后字节没有输出,\n并且不为零。请与作者联系。", "文件解压",
							JOptionPane.ERROR_MESSAGE);
					System.exit(0);
					// 出现错误,最后一个字节没有输出
				}
			} else {
				if (nowBinary.isLeaf()) {
					output.writeByte(nowBinary.getByteToCoded());// 将最后一个字符输出
				} else {
					JOptionPane.showMessageDialog(null,
							"解压文件时出现未知错误,最后字节没有输出,\n请与作者联系。", "文件解压",
							JOptionPane.ERROR_MESSAGE);
					System.exit(0);
					// 出现错误,最后一个字节没有输出
				}
			}
			input.close();
			output.close();
		} catch (FileNotFoundException fnfe) {
			JOptionPane.showMessageDialog(null, "读取文件时出现错误,没有找到相关文件,\n请重新运行。",
					"文件压缩", JOptionPane.ERROR_MESSAGE);
			System.exit(0);
			// 找不到文件
		} catch (EOFException eofe) {
			JOptionPane.showMessageDialog(null, "读取文件时出现错误,超出文件大小,\n压缩文件可能已损坏,请重新运行。",
					"文件压缩", JOptionPane.ERROR_MESSAGE);
			System.exit(0);
		} catch (IOException ioe) {
			JOptionPane.showMessageDialog(null, "读取文件时出现错误,请重新运行。", "文件压缩",
					JOptionPane.ERROR_MESSAGE);
			System.exit(0);
			// 读取文件失败
		}

	}
}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -