哈弗曼编码对文件进行压缩解压

头文件:HuffmanCoding.h

#ifndef HUFFMANCODING_H 
#define HUFFMANCODING_H 
#include <iostream> 
#include "HuffTree.h" 
using namespace std;
//栈结构 
struct Link {
unsigned int element;
Link *next;
Link(const unsigned int &elemval, Link *nextval = NULL);
Link(Link *nextval = NULL);
};
struct LStack {
Link* top;
int size;
LStack() { size = 0; top = NULL; }
~LStack() { clear(); }
void clear();
bool push(const unsigned int &item);
bool pop(unsigned int &it);
};


//缓冲区 
struct Buffer {
unsigned char byte; //表示字节,每个字节由8个位组成 
unsigned int bits; //表示位(比特) 
void clear(); //清空缓存区 
};
//临时保存叶子结点的数域 
struct Save {
unsigned char ch;
unsigned long int val;
};
class HuffmanCoding {
private:
SAList *list, *copy;//有序表 
HuffTree* tree; //哈夫曼树 
Buffer buffer; //缓存区 
LStack *stack; //栈 
Save save; //临时保存叶子结点的数域 
FILE *sourceFile; //表示源文件 
FILE *targetFile; //表示目标文件 
unsigned long int total; //表示要进行编码的文件的总字节数(number < 4290000000) 
unsigned long int ch[257]; //表示可以编码的所有字符,其下标表示对应的字符,对应下标的数组值表示该字符的权值 
unsigned int numOfLeaf; //表示需要建立叶子结点的个数 
public:
void code(); //对文件内容进行编码 
void decode(); //对文件内容进行译码 
void buildSAList(); //建立有序表,并将建立有序表的信息保存到目标文件 
void write(unsigned int c); //利用建立的缓存,向目标文件中输出字符 
void exportSAList(); //压缩文件中导出有序表 
unsigned int read(); //利用建立的缓存,从压缩文件提取字符 
};
#endif 


头文件:HuffTree.h

#ifndef HUFFTREE_H 
#define HUFFTREE_H 

//由于SAList类要用到HuffTree类,此处即为虚定义 
class HuffTree;


//有序表类的声明 
class SAList {
int fence;
int maxSize;
int listSize;
public:
SAList(int size = 0);
~SAList();
HuffTree* *listArray;
void setStart();
void next();
int getLength();
bool insert(HuffTree* item); //排序插入 
HuffTree* remove();
bool lessThan(HuffTree* x, HuffTree* y);
};


//哈弗曼树结点的类声明 
class HuffTreeNode {
public:
unsigned char value;
unsigned long int weight;
unsigned int leftOrRight; //若为左儿子,则值为0,若为右儿子,则值为1 
HuffTreeNode *parent;
HuffTreeNode *leftChild;
HuffTreeNode *rightChild;
bool isLeaf;
};


//哈夫曼树的声明 
class HuffTree {
public:
HuffTreeNode* subRoot;
HuffTree();
HuffTree(unsigned char val, unsigned long int freq);
HuffTree(HuffTree* l, HuffTree* r);
HuffTree* buildHuffTree(SAList *sal, SAList *copy);
};
#endif


源文件:HuffmanCoding.cpp

 #include <fstream> 
#include <iostream> 
#include "HuffmanCoding.h" 
using namespace std;


Link::Link(const unsigned int &elemval, Link *nextval) {
element = elemval;
next = nextval;
}


Link::Link(Link *nextval) {
next = nextval;
}


void LStack::clear() {
while (top != NULL){
Link *temp = top;
top = top->next;
delete temp;
}
size = 0;
}


bool LStack::push(const unsigned int &item) {
top = new Link(item, top);
size++;
return true;
}


bool LStack::pop(unsigned int &it) {
if (size == 0) return false;
it = top->element;
Link *temp = top;
top = top->next;
size--;
delete temp;
return true;
}


void Buffer::clear() {
bits = 0;
byte = 0;
}


void HuffmanCoding::code() {
char *sourceFileName = new char[];
char *targetFileName = new char[];
total = 0;
numOfLeaf = 0;


cout << "请输入源文件的文件名:(支持最大文件为4.29G)";
cin >> sourceFileName;
sourceFile = fopen(sourceFileName, "rb");
//判断源文件是否存在,不存在则要求用户重新输入 
while (sourceFile == NULL) {
cout << "您输入的源文件不存在!" << "\n请重新输入:";
cin >> sourceFileName;
sourceFile = fopen(sourceFileName, "rb");
}


fgetc(sourceFile);
//判断文件内容是否为空,若为空则退出程序 
if (feof(sourceFile)) {
cout << "文件内容为空!";
system("PAUSE");
exit(-1);
}


cout << "请输入目标文件的文件名:";
cin >> targetFileName;
targetFile = fopen(targetFileName, "wb");
//判断目标文件是否可以建立 
while (targetFile == NULL) {
cout << "目标文件无法建立!" << "\n请重新输入:";
cin >> targetFileName;
targetFile = fopen(targetFileName, "wb");
}
cout << "文件压缩中...\n\n";


buildSAList(); //建立有序表,并保存编码信息 
tree = tree->buildHuffTree(list, copy); //利用已建立的有序表,建立哈夫曼树 


//开始编码,并将编码后的内容输出到目标文件 
rewind(sourceFile); //将文件指针重新指向文件内容起始处 
unsigned char tempChar = fgetc(sourceFile); //取文件内容的下一个字符 
unsigned int tempInt;
HuffTreeNode *tempTreeNode;
stack = new LStack();
buffer.clear(); //清空缓存区 
//当文件内容全部被扫描完,循环结束 
while (!feof(sourceFile)) {
//搜索匹配tempChar的叶子的值 
for (int i = 0; i< copy->getLength(); i++) {
if (tempChar == copy->listArray[i]->subRoot->value) {
stack->clear();
tempTreeNode = copy->listArray[i]->subRoot;
while (tempTreeNode != tree->subRoot) {
stack->push(tempTreeNode->leftOrRight);
tempTreeNode = tempTreeNode->parent;
}//end while 
while (stack->pop(tempInt)) {
write(tempInt);
}
break;
}//end if 
}//end for 
tempChar = fgetc(sourceFile);
}//end while 


//如果缓存区还有剩余的位,则添加0到缓存区,直到够满8个位建立个字符,并向目标文件写入该字符 
if (buffer.bits > 0) {
for (unsigned int i = buffer.bits; i<8; i++)
write(0);
}
cout << "文件压缩完毕" << endl;


fseek(sourceFile, 0L, SEEK_END);
double size1 = ftell(sourceFile);
fseek(targetFile, 0L, SEEK_END);
double size2 = ftell(targetFile);
cout << "压缩比例为" << size2 / size1 << endl;

delete stack;
delete list;
fclose(sourceFile); //关闭文件流 
fclose(targetFile);
}


void HuffmanCoding::decode() {
char *sourceFileName = new char[];
char *targetFileName = new char[];
total = 0;
numOfLeaf = 0;


cout << "\n请输入压缩文件的文件名:";
cin >> sourceFileName;
sourceFile = fopen(sourceFileName, "rb");
//判断源文件是否存在,不存在则要求用户重新输入 
while (sourceFile == NULL) {
cout << "您输入的压缩文件不存在!" << "\n请重新输入:";
cin >> sourceFileName;
sourceFile = fopen(sourceFileName, "rb");
}


fgetc(sourceFile);
//判断文件内容是否为空,若为空则退出程序 
if (feof(sourceFile)) {
cout << "文件内容为空!";
system("PAUSE");
exit(-1);
}

cout << "请输入目标文件的文件名:";
cin >> targetFileName;
targetFile = fopen(targetFileName, "wb");
//判断目标文件是否可以建立 

while (targetFile == NULL) {
cout << "目标文件无法建立!" << "\n请重新输入:";
cin >> targetFileName;
targetFile = fopen(targetFileName, "wb");
}
cout << "文件解压中...\n\n";
rewind(sourceFile); //将源文件指针重新指向文件起始处 
exportSAList(); //导出叶子结点有序表 
tree = tree->buildHuffTree(list, copy); //利用已建立的有序表,建立哈夫曼树 

//译码开始 
buffer.clear(); //清空缓存区 
unsigned int tempInt;
HuffTreeNode *tempTreeNode;
while (total > 0) {
tempTreeNode = tree->subRoot;
while (!tempTreeNode->isLeaf) {
tempInt = read();
if (tempInt == 0) {
tempTreeNode = tempTreeNode->leftChild;
}
else {
tempTreeNode = tempTreeNode->rightChild;
}
}
fputc(tempTreeNode->value, targetFile);
total--;
}
cout << "文件解压完毕" << endl;
delete list;
fclose(sourceFile);
fclose(targetFile);
}

//按字符扫描源文件,统计源文件出现的字符及其权值 
//利用统计出的字符和和其权值建立叶子结点有序表,并将统计出的信息保存到目标文件 
void HuffmanCoding::buildSAList() {
unsigned char tempChar;
HuffTree *temp;
//初始化储存字符的数组 
for (int i = 0; i<257; i++)
ch[i] = 0;
rewind(sourceFile); //将文件指针重新指向文件内容起始处 
tempChar = fgetc(sourceFile); //取文件内容的下一个字符 
//当文件内容全部被扫描完,循环结束 
while (!feof(sourceFile)) {
++ch[tempChar];
++total;
tempChar = fgetc(sourceFile);
}
//计算应该建立的叶子结点总数 
for (int j = 0; j<257; j++) {
if (ch[j] > 0)
numOfLeaf++;
}
//将源文件的字符总数及叶子结点总数保存到目标文件 
fwrite(&total, sizeof(unsigned long int), 1, targetFile);
fwrite(&numOfLeaf, sizeof(unsigned int), 1, targetFile);
//建立叶子结点有序表 
list = new SAList(numOfLeaf);
copy = new SAList(numOfLeaf);
//依次扫描ch,将权值不为0的字符插入到叶子结点有序表中 
//并将此字符及其权值保存到目标文件 
for (int k = 0; k<257; k++) {
if (ch[k] > 0) {
temp = new HuffTree(k, ch[k]);
list->insert(temp);
save.ch = k;
save.val = ch[k];
fwrite(&save, sizeof(save), 1, targetFile);
}
}
}

//利用建立的缓存,向目标文件中输出字符 
void HuffmanCoding::write(unsigned int c) {
++buffer.bits;
buffer.byte = (buffer.byte << 1) + c; //将byte左移一位,并在末位加上c 
//如果byte的8个位都被写满,则向目标文件输出byte,并清空缓存区 
if (buffer.bits == 8) {
fputc(buffer.byte, targetFile);
buffer.clear();
}
}

//从压缩文件中导出叶子结点有序表 
void HuffmanCoding::exportSAList() {
HuffTree *temp;
fread(&total, sizeof(unsigned long int), 1, sourceFile);
fread(&numOfLeaf, sizeof(unsigned int), 1, sourceFile);
list = new SAList(numOfLeaf);
for (int i = 0; i<numOfLeaf; i++) {
fread(&save, sizeof(save), 1, sourceFile);
temp = new HuffTree(save.ch, save.val);
list->insert(temp);
}
}
//从压缩文件中读取一个比特 
unsigned int HuffmanCoding::read() {
if (buffer.bits == 0) {
buffer.bits = 8;
buffer.byte = fgetc(sourceFile);
}
if (buffer.byte & (1 << (buffer.bits - 1))) {
buffer.bits--;
return 1;
}
else {
buffer.bits--;
return 0;
}
}


源文件:HuffTree.cpp

#include <iostream> 
#include "HuffTree.h" 


//有序表类的声明 
SAList::SAList(int size) {
maxSize = size;
listSize = fence = 0;
listArray = new HuffTree*[maxSize];
}


SAList::~SAList(){
delete[]listArray;
}


void SAList::setStart() {
fence = 0;
}


void SAList::next() {
if (fence <= listSize)
++fence;
}


int SAList::getLength() {
return listSize;
}


//排序插入 
bool SAList::insert(HuffTree* item) {
if (listSize == maxSize)
return false;


HuffTree *current;
for (fence = 0; fence<listSize; fence++) {
current = listArray[fence];
if (!lessThan(current, item))
break;
}
for (int i = listSize; i>fence; i--) {
listArray[i] = listArray[i - 1];
}
listArray[fence] = item;
++listSize;
return true;
}


HuffTree* SAList::remove() {
if (listSize == fence) {
return NULL;
}
HuffTree *temp = listArray[fence];
for (int i = fence; i<listSize - 1; ++i) {
listArray[i] = listArray[i + 1];
}
--listSize;
return temp;
}


bool SAList::lessThan(HuffTree* x, HuffTree* y) {
return x->subRoot->weight < y->subRoot->weight;
}


//哈夫曼树的实现 
HuffTree::HuffTree() {
subRoot = new HuffTreeNode;
subRoot->weight = 0;
}


HuffTree::HuffTree(unsigned char val, unsigned long int freq) {
subRoot = new HuffTreeNode;
subRoot->value = val;
subRoot->weight = freq;
subRoot->isLeaf = true;
}


HuffTree::HuffTree(HuffTree* l, HuffTree* r) {
subRoot = new HuffTreeNode;
subRoot->leftChild = l->subRoot;
subRoot->rightChild = r->subRoot;
subRoot->leftChild->parent = subRoot->rightChild->parent = subRoot;
subRoot->weight = l->subRoot->weight + r->subRoot->weight;
subRoot->isLeaf = false;
}


HuffTree* HuffTree::buildHuffTree(SAList *sal, SAList *copy) {
HuffTree *temp1, *temp2, *temp3;
temp1 = temp2 = temp3 = new HuffTree;
for (int i = sal->getLength(); i>1; i--) {
sal->setStart();
temp1 = sal->remove();
temp2 = sal->remove();
temp1->subRoot->leftOrRight = 0;
temp2->subRoot->leftOrRight = 1;
temp3 = new HuffTree(temp1, temp2);
if (temp1->subRoot->isLeaf)
copy->insert(temp1);
if (temp2->subRoot->isLeaf)
copy->insert(temp2);
sal->insert(temp3);
}
return temp3;
}


源文件:Driver.cpp

#include "HuffmanCoding.h" 


int main() {
HuffmanCoding h;
int i = 1;
while (i)
{
h.code();
cout << "输入0退出压缩,输入1继续压缩";
cin >> i;
}
int j=1;
while (j)
{
h.decode();
cout << "输入0退出解压,输入1继续解压";
cin >> i;
}


system("PAUSE");
return 0;
}


运行结果如下图:



  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
综合实验: 1. 问题描述 利用哈夫曼编码进行通信可以大大提高信道利用率,缩短信息传输时间,降低传输成本。这要求在发送端通过一个编码系统对待传输数据预先编码,在接收端将传来的数据进行译码(复原)。对于双工信道(即可以双向传输信息的信道),每端都需要一个完整的编/译码系统。试为这样的信息收发站编写一个哈夫曼码的编/译码系统。 2. 基本要求 一个完整的系统应具有以下功能: (1) I:初始化(Initialization)。从终端读入字符集大小n,以及n个字符和n个权值,建立哈夫曼树,并将它存于文件hfmTree中。 (2) E:编码(Encoding)。利用已建好的哈夫曼树(如不在内存,则从文件hfmTree中读入),对文件ToBeTran中的正文进行编码,然后将结果存入文件CodeFile中。 (3) D:译码(Decoding)。利用已建好的哈夫曼树将文件CodeFile中的代码进行译码,结果存入文件Textfile中。 (4) P:印代码文件(Print)。将文件CodeFile以紧凑格式显示在终端上,每行50个代码。同时将此字符形式的编码文件写入文件CodePrin中。 (5) T:印哈夫曼树(Tree printing)。将已在内存中的哈夫曼树以直观的方式(比如树)显示在终端上,同时将此字符形式的哈夫曼树写入文件TreePrint 中。 3. 测试数据 用下表给出的字符集和频度的实际统计数据建立哈夫曼树,并实现以下报文的编码和译码:“THIS PROGRAME IS MY FAVORITE”。 字符 A B C D E F G H I J K L M 频度 186 64 13 22 32 103 21 15 47 57 1 5 32 20 字符 N O P Q R S T U V W X Y Z 频度 57 63 15 1 48 51 80 23 8 18 1 16 1
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值