C++项目基于HuffmanTree实现文件的压缩与解压缩功能

标题:C++项目基于HuffmanTree实现文件的压缩与解压缩功能

一、HuffmanTree基本概念

Huffman编码是一种无损压缩算法,主要思想是利用频率较高的字符使用较短的二进制编码,频率较低的字符使用较长的二进制编码,从而实现压缩目的。

Huffman树是一种高效的实现Huffman编码的数据结构,它是一棵带权树,其中每个叶子结点代表一个字符,其权值为该字符在文本中出现的频率。利用HuffmanTree的思想,我们可以在不降低原始数据质量的情况下实现对文本的压缩。

二、项目实现过程

下面介绍C++项目基于HuffmanTree实现文件的压缩与解压缩功能的详细实现步骤。

1. 遍历文件并生成字符频率map

首先,我们需要遍历所要压缩的文件,并统计文件中每个字符出现的频率。这里使用std::map<char, int>来记录字符与出现频率的对应关系。

示例代码:

#include <fstream>
#include <map>

// 遍历文件并生成字符频率map
std::map<char, int> getCharFrequency(std::string filePath) {
    std::map<char, int> frequencyMap;
    std::ifstream file(filePath, std::ios::binary);
    char c;
    while (file.get(c)) {
        ++frequencyMap[c];
    }
    return frequencyMap;
}

2. 根据字符频率map构建Huffman树

获取到字符频率map后,我们需要构建Huffman树。具体实现方法为:

  1. 将频率map中的字符和权重都存入一个vector中;
  2. 构建小根堆,按照每个字符出现频率的大小进行排序;
  3. 从小根堆获取最小的两个节点,创建一个新的父节点,权重为两个节点的权重之和,并将父节点插入回小根堆;
  4. 重复第三步,直到小根堆只剩下一个节点,即Huffman树的根节点。

示例代码:

#include <queue>
#include <vector>

// Huffman树节点结构体
struct Node {
    char character;
    int weight;
    Node *leftChild;
    Node *rightChild;
    Node(char c, int w, Node *l = nullptr, Node *r = nullptr) : character(c), weight(w), leftChild(l), rightChild(r) {}
};

// 小根堆比较器
struct compareNode {
    bool operator()(const Node *a, const Node *b) { return a->weight > b->weight; }
};

// 根据字符频率map构建Huffman树
Node *buildHuffmanTree(std::map<char, int> frequencyMap) {
    std::priority_queue<Node *, std::vector<Node *>, compareNode> minHeap;
    for (const auto &frequency : frequencyMap) {
        minHeap.push(new Node(frequency.first, frequency.second));
    }
    while (minHeap.size() > 1) {
        Node *leftChild = minHeap.top();
        minHeap.pop();
        Node *rightChild = minHeap.top();
        minHeap.pop();
        minHeap.push(new Node('\0', leftChild->weight + rightChild->weight, leftChild, rightChild));
    }
    return minHeap.top();
}

3. 生成Huffman编码表

生成Huffman编码表的过程是将Huffman树进行遍历,并在遍历过程中记录下每个字符的Huffman编码。在这里我们使用递归的方式来进行Huffman树的遍历。

示例代码:

#include <unordered_map>

// 生成Huffman编码表的递归函数
void buildHuffmanCode(Node *node, std::string code, std::unordered_map<char, std::string> &codeTable) {
    if (node->leftChild == nullptr && node->rightChild == nullptr) {
        codeTable[node->character] = code;
        return;
    }
    buildHuffmanCode(node->leftChild, code + "0", codeTable);
    buildHuffmanCode(node->rightChild, code + "1", codeTable);
}

// 生成Huffman编码表
std::unordered_map<char, std::string> buildHuffmanCodeTable(Node *huffmanTree) {
    std::unordered_map<char, std::string> codeTable;
    buildHuffmanCode(huffmanTree, "", codeTable);
    return codeTable;
}

4. 将文件按照Huffman编码进行压缩

获取到Huffman编码表后,我们就可以将所要压缩的文件按照这个编码表进行压缩。具体实现方法是:

  1. 遍历文件,获取到每个字符对应的Huffman编码;
  2. 将Huffman编码依次存入一个字符串中,每8个二进制位组成一个字节,并将其写入到文件中;
  3. 在文件的开头写入字符频率map,以便解压缩时可以重新生成Huffman树。

示例代码:

#include <bitset>
#include <fstream>

// 将文件按照Huffman编码进行压缩
void compressFile(std::string sourcePath, std::string targetPath) {
    auto frequencyMap = getCharFrequency(sourcePath);
    auto huffmanTree = buildHuffmanTree(frequencyMap);
    auto codeTable = buildHuffmanCodeTable(huffmanTree);
    std::ifstream inputFile(sourcePath, std::ios::binary);
    std::ofstream outputFile(targetPath, std::ios::binary);
    // 写入字符频率map
    size_t frequencyMapSize = frequencyMap.size();
    outputFile.write((char *)&frequencyMapSize, sizeof(size_t));
    for (const auto &frequency : frequencyMap) {
        outputFile.write(&frequency.first, sizeof(char));
        outputFile.write((char *)&frequency.second, sizeof(int));
    }
    // 将文件按照Huffman编码进行压缩
    std::string bitString;
    char c;
    while (inputFile.get(c)) {
        bitString += codeTable[c];
        while (bitString.length() >= 8) {
            std::bitset<8> byte(bitString.substr(0, 8));
            outputFile.write((char *)&byte, sizeof(char));
            bitString.erase(0, 8);
        }
    }
    if (!bitString.empty()) {
        bitString += std::string(8 - bitString.length() % 8, '0');
        std::bitset<8> byte(bitString.substr(0, 8));
        outputFile.write((char *)&byte, sizeof(char));
    }
}

5. 解压缩文件

将文件按照Huffman编码进行压缩后,我们需要实现对其进行解压缩。具体实现方法是:

  1. 读取文件开头的字符频率map,重新生成Huffman树;
  2. 加载文件中的Huffman编码,并根据Huffman编码还原出文件中的原始字符。

示例代码:

#include <fstream>

// 解压缩文件
void decompressFile(std::string sourcePath, std::string targetPath) {
    std::ifstream inputFile(sourcePath, std::ios::binary);
    size_t frequencyMapSize;
    // 读取字符频率map
    inputFile.read((char *)&frequencyMapSize, sizeof(size_t));
    std::map<char, int> frequencyMap;
    for (size_t i = 0; i < frequencyMapSize; ++i) {
        char c;
        int frequency;
        inputFile.read(&c, sizeof(char));
        inputFile.read((char *)&frequency, sizeof(int));
        frequencyMap[c] = frequency;
    }
    // 构建Huffman树并重新生成Huffman编码表
    auto huffmanTree = buildHuffmanTree(frequencyMap);
    auto codeTable = buildHuffmanCodeTable(huffmanTree);
    std::ofstream outputFile(targetPath, std::ios::binary);
    // 解压缩文件
    std::string bitString;
    char c;
    while (inputFile.get(c)) {
        std::bitset<8> byte(c);
        bitString += byte.to_string();
    }
    if (bitString.length() % 8 != 0) {
        bitString.erase(bitString.length() - (bitString.length() % 8));
    }
    Node *currentNode = huffmanTree;
    for (size_t i = 0; i < bitString.length(); ++i) {
        if (bitString[i] == '0') {
            currentNode = currentNode->leftChild;
        } else {
            currentNode = currentNode->rightChild;
        }
        if (currentNode->leftChild == nullptr && currentNode->rightChild == nullptr) {
            outputFile.write(&currentNode->character, sizeof(char));
            currentNode = huffmanTree;
        }
    }
}

三、示例说明

下面给出两个压缩和解压缩文件的示例:

示例一

压缩文件test.txt并将其解压到output.txt中。

std::string sourcePath = "test.txt";
std::string compressedPath = "compressed.bin";
std::string decompressedPath = "output.txt";
compressFile(sourcePath, compressedPath);
decompressFile(compressedPath, decompressedPath);

示例二

压缩文件test.mov并将其解压到output.mov中。

std::string sourcePath = "test.mov";
std::string compressedPath = "compressed.bin";
std::string decompressedPath = "output.mov";
compressFile(sourcePath, compressedPath);
decompressFile(compressedPath, decompressedPath);

以上就是C++项目基于HuffmanTree实现文件的压缩与解压缩功能的完整攻略。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:C++项目基于HuffmanTree实现文件的压缩与解压缩功能 - Python技术站

(0)
上一篇 2023年6月27日
下一篇 2023年6月27日

相关文章

  • 分享我对JS插件开发的一些感想和心得

    分享我对JS插件开发的一些感想和心得 简介 JS插件开发是一项非常有趣和有挑战性的任务。它允许开发者将自己的功能模块化,并与其他开发者共享和重用。在本攻略中,我将分享一些关于JS插件开发的感想和心得,希望对你有所帮助。 1. 设计插件接口 在开发JS插件时,良好的接口设计是至关重要的。一个好的接口可以提供清晰的使用方式,并减少与其他代码的耦合。以下是一个示例…

    other 2023年7月27日
    00
  • Win11截图工具“此应用程序无法打开”怎么办?(附解决方法)

    针对“Win11截图工具“此应用程序无法打开”的问题,以下是详细的攻略,具体步骤如下: 问题描述 用户在使用Win11截图工具时,可能会遇到某些无法打开的情况,系统会提示:“此应用程序无法打开”。 解决方法 方法一:检查系统更新 第一种方法是检查系统更新,因为Win11截图工具是Win11系统中自带的工具,如果系统存在严重的问题就会影响其正常运行。以下是操作…

    other 2023年6月25日
    00
  • 解读C++11 原生字符串

    下面是“解读C++11 原生字符串”的完整攻略: 什么是C++11原生字符串? C++11中引入了一种新的字符串类型,叫做原生字符串(Raw String)。它不需要转义字符,可以包含任何字符,包括换行符等特殊字符。 举个例子,我们来看一下使用传统字符串和原生字符串表示同样的字符串的区别。 传统字符串: cout << "Hello\t…

    other 2023年6月20日
    00
  • vue3升级常见问题详细汇总

    Vue3升级常见问题详细汇总 Vue3作为一个全新的版本,对于Vue2用户来说需要注意一些变化和更新。本文将为大家汇总Vue3升级过程中的常见问题,并介绍一些常见的解决方案。 问题1: 修改了”v-model”指令 在Vue2中,”v-model”指令可以用于双向绑定数据。但在Vue3中,”v-model”指令的用法发生了修改。如下所示: <!– V…

    other 2023年6月27日
    00
  • Angular 作用域scope的具体使用

    Angular 作用域(scope)的具体使用攻略 Angular 是一个流行的前端框架,它使用作用域(scope)来管理数据和状态。作用域(scope)是一个对象,它绑定了视图和控制器(controller)之间的通信。在本攻略中,我们将详细讲解 Angular 作用域(scope)的具体使用。 1. 创建作用域(scope) 在 Angular 中,可以…

    other 2023年8月19日
    00
  • 我叫MT最应该先做哪张橙卡 橙卡进化优先级详细分析

    我叫MT最应该先做哪张橙卡 橙卡进化优先级详细分析攻略 目录 引言 进化优先级原则 示例一:橙卡A的进化优先级分析 示例二:橙卡B的进化优先级分析 总结 1. 引言 在我叫MT游戏中,橙卡是非常重要的进化卡牌。选择正确的橙卡先进行进化对于玩家的发展至关重要。本攻略将详细介绍如何确定橙卡的进化优先级,并通过两个示例进行说明。 2. 进化优先级原则 技能效果:考…

    other 2023年6月28日
    00
  • 浅谈SpringBoot如何自定义Starters

    下面我来详细讲解“浅谈SpringBoot如何自定义Starters”的完整攻略。 什么是Starters Starters是SpringBoot的一个重要特性,它是SpringBoot在多个场景中预先定义的一组依赖包和默认配置。当我们创建SpringBoot应用时,只需要根据自己的需求添加对应的Starter依赖,就可以快速构建出符合要求的应用程序。 比如…

    other 2023年6月25日
    00
  • 网站访问慢的排查方法及解决方案

    网站访问慢的排查方法及解决方案 排查方法 1. 确定问题范围 首先需要明确问题的具体表现,例如是整个网站慢还是只有某个页面慢,是移动端还是PC端访问慢等等。通过定位问题的具体表现,可以明确排查范围,缩小问题的影响范围从而更加高效地排查问题。 2. 基础排查 基础排查包括检查网站服务器、网络连接、DNS解析等基本内容,以下是一些基础排查的方法: 通过ping命…

    other 2023年6月26日
    00
合作推广
合作推广
分享本页
返回顶部