📄 compress.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 + -