C++写利用Huffman编码实现文件的压缩与解压缩,要求能够测试压缩率和解压缩速度
时间: 2024-03-27 22:38:00 浏览: 100
好的,我可以帮你回答这个问题。首先,Huffman编码是一种无损数据压缩算法,它可以将一些出现频率较高的字符用较短的编码表示,从而达到压缩数据的目的。下面是一些实现Huffman编码的基本步骤:
1. 统计字符出现频率,生成Huffman树。
2. 根据Huffman树生成编码表。
3. 将编码表写入压缩文件头部。
4. 读取原文件,将每个字符转换为对应的Huffman编码,并写入输出文件。
5. 解压缩时,读取压缩文件头部的编码表,根据编码表将Huffman编码转换为字符。
下面是一个用C++实现Huffman编码压缩和解压缩的示例代码:
```cpp
#include <iostream>
#include <fstream>
#include <queue>
#include <unordered_map>
#include <vector>
#include <bitset>
#include <ctime>
using namespace std;
// Huffman树节点
struct HuffmanNode
{
char ch;
int freq;
HuffmanNode *left, *right;
HuffmanNode(char ch, int freq) : ch(ch), freq(freq), left(nullptr), right(nullptr) {}
};
// 比较函数,用于构建Huffman树
struct Compare
{
bool operator()(const HuffmanNode* a, const HuffmanNode* b) const
{
return a->freq > b->freq;
}
};
// 统计字符出现频率
unordered_map<char, int> getCharFreq(const string& input)
{
unordered_map<char, int> freq;
for (char ch : input)
{
++freq[ch];
}
return freq;
}
// 构建Huffman树
HuffmanNode* buildHuffmanTree(const unordered_map<char, int>& freqMap)
{
priority_queue<HuffmanNode*, vector<HuffmanNode*>, Compare> pq;
for (auto& item : freqMap)
{
pq.push(new HuffmanNode(item.first, item.second));
}
while (pq.size() > 1)
{
HuffmanNode* left = pq.top();
pq.pop();
HuffmanNode* right = pq.top();
pq.pop();
HuffmanNode* parent = new HuffmanNode('\0', left->freq + right->freq);
parent->left = left;
parent->right = right;
pq.push(parent);
}
return pq.top();
}
// 生成编码表
void generateEncodingTable(HuffmanNode* root, unordered_map<char, string>& encodingTable, string code)
{
if (!root) return;
if (root->ch != '\0')
{
encodingTable[root->ch] = code;
}
generateEncodingTable(root->left, encodingTable, code + "0");
generateEncodingTable(root->right, encodingTable, code + "1");
}
// 编码
string encode(const string& input, const unordered_map<char, string>& encodingTable)
{
string encoded;
for (char ch : input)
{
encoded += encodingTable.at(ch);
}
return encoded;
}
// 解码
string decode(const string& encoded, HuffmanNode* root)
{
string decoded;
HuffmanNode* node = root;
for (char bit : encoded)
{
if (bit == '0')
{
node = node->left;
}
else
{
node = node->right;
}
if (node->ch != '\0')
{
decoded += node->ch;
node = root;
}
}
return decoded;
}
// 将编码表写入文件头部
void writeEncodingTable(const unordered_map<char, string>& encodingTable, ofstream& outfile)
{
for (auto& item : encodingTable)
{
outfile << item.first << item.second << endl;
}
}
// 从文件头部读取编码表
unordered_map<string, char> readEncodingTable(ifstream& infile)
{
unordered_map<string, char> encodingTable;
string line;
while (getline(infile, line))
{
char ch = line[0];
string code = line.substr(1);
encodingTable[code] = ch;
}
return encodingTable;
}
// 压缩文件
void compressFile(const string& inputFilename, const string& outputFilename)
{
// 读取原文件
ifstream infile(inputFilename, ios::in | ios::binary);
if (!infile)
{
cerr << "Failed to open input file " << inputFilename << endl;
return;
}
string input((istreambuf_iterator<char>(infile)), istreambuf_iterator<char>());
infile.close();
// 统计字符出现频率
unordered_map<char, int> freqMap = getCharFreq(input);
// 构建Huffman树
HuffmanNode* root = buildHuffmanTree(freqMap);
// 生成编码表
unordered_map<char, string> encodingTable;
generateEncodingTable(root, encodingTable, "");
// 将编码表写入文件头部
ofstream outfile(outputFilename, ios::out | ios::binary);
if (!outfile)
{
cerr << "Failed to open output file " << outputFilename << endl;
return;
}
writeEncodingTable(encodingTable, outfile);
// 编码并写入输出文件
string encoded = encode(input, encodingTable);
int padding = 8 - (encoded.size() % 8);
string paddingStr(padding, '0');
outfile << paddingStr << encoded;
outfile.close();
}
// 解压缩文件
void decompressFile(const string& inputFilename, const string& outputFilename)
{
// 读取压缩文件头部的编码表
ifstream infile(inputFilename, ios::in | ios::binary);
if (!infile)
{
cerr << "Failed to open input file " << inputFilename << endl;
return;
}
unordered_map<string, char> encodingTable = readEncodingTable(infile);
// 读取压缩文件内容
string encoded((istreambuf_iterator<char>(infile)), istreambuf_iterator<char>());
infile.close();
// 解码
int padding = stoi(encoded.substr(0, 8), nullptr, 2);
string decoded = decode(encoded.substr(8), buildHuffmanTree(encodingTable), padding);
// 写入输出文件
ofstream outfile(outputFilename, ios::out | ios::binary);
if (!outfile)
{
cerr << "Failed to open output file " << outputFilename << endl;
return;
}
outfile << decoded;
outfile.close();
}
int main()
{
string inputFilename = "input.txt";
string compressedFilename = "compressed.bin";
string decompressedFilename = "decompressed.txt";
// 压缩文件
clock_t start = clock();
compressFile(inputFilename, compressedFilename);
clock_t end = clock();
cout << "Compressed file created." << endl;
cout << "Compression time: " << (double)(end - start) / CLOCKS_PER_SEC << " seconds." << endl;
// 解压缩文件
start = clock();
decompressFile(compressedFilename, decompressedFilename);
end = clock();
cout << "Decompressed file created." << endl;
cout << "Decompression time: " << (double)(end - start) / CLOCKS_PER_SEC << " seconds." << endl;
// 计算压缩率
ifstream infile(inputFilename, ios::in | ios::binary);
long long inputFileSize = infile.seekg(0, ios::end).tellg();
infile.close();
ifstream compressedFile(compressedFilename, ios::in | ios::binary);
long long compressedFileSize = compressedFile.seekg(0, ios::end).tellg();
compressedFile.close();
double compressionRatio = (double)compressedFileSize / inputFileSize;
cout << "Compression ratio: " << compressionRatio * 100 << "%" << endl;
return 0;
}
```
在上面的示例代码中,我们用了一个简单的文本文件作为输入文件,使用了`ifstream`和`ofstream`来读写文件。我们还用了`clock`函数来计算压缩和解压缩文件所需的时间,以及计算压缩率。
阅读全文