c++ - 哈夫曼压缩器/解压器

标签 c++ memory-management c++14 smart-pointers huffman-code

代码已更新为使用 unique_ptr 和命名空间。 注意:我试图在命名空间 huffman 中实现匿名命名空间,但它不允许将文件分成 .cpp 和 .h。欢迎对当前代码提出任何批评。请随意使用 MIT 协议(protocol)中规定的代码。

源代码.cpp:

/*
#######################################################################################################################################
Copyright 2017 Daniel Rossinsky

Permission is hereby granted, free of charge, to any person obtaining a copy of this software
and associated documentation files (the "Software"), to deal in the Software without restriction,
including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#######################################################################################################################################
*/

#include"Huffman.h"

int main(int argc, char *argv[]) {

    if (argc < 4) std::cout << "Too few arguments\n";
    else if (argc == 4) {
        if (*argv[1] == 'c') Huffman::compress(argv[2], argv[3], argv[3]);
        else if (*argv[1] == 'd') {
            std::string temp{ argv[2] };
            std::size_t pathEnd{ temp.find_last_of("/\\") };
            Huffman::decompress(argv[2], argv[3], temp.substr(0, pathEnd + 1));
        }//end of else if
        else std::cout << "Unknown command\n";
    }//end of else if
    else std::cout << "Too much arguments\n";
    return 0;

    //Huffman::compress("C:/Users/User/Desktop/test.txt", "C:/Users/User/Desktop/", "C:/Users/User/Desktop/");
    //Huffman::decompress("C:/Users/User/Desktop/testCompressed.bin", "C:/Users/User/Desktop/testKey.bin", "C:/Users/User/Desktop/");
}

/*
cmd example:
-----------
compress:
syntax: huffman.exe c filePath dest
example: C:/Users/User/Desktop/huffman.exe c C:/Users/User/Desktop/test.txt C:/Users/User/Desktop/

decompress:
syntax: huffman.exe d filePath keyPath
example: C:/Users/User/Desktop/huffman.exe d C:/Users/User/Desktop/testCompressed.bin C:/Users/User/Desktop/testKey.bin

NOTE:
-----
You can use the commented code in main instead
*/

哈夫曼.h:

#ifndef HUFFMAN
#define HUFFMAN

#include<iostream>
#include<map>
#include<vector>
#include<string>
#include<deque>
#include<memory>

namespace Huffman {
    namespace inner {
        struct node;

        /*type aliases*/
        using Table     = std::map<char, std::size_t>;
        using Cypher    = std::map<char, std::vector<bool> >;
        using smartNode = std::unique_ptr<node>;
        /*type aliases*/

        struct node {
            smartNode   m_left;
            smartNode   m_right;
            std::size_t m_frequency{};
            char        m_data{};

            node() = default;
            node(smartNode left, smartNode right) :
                m_left{ std::move(left) }, m_right{ std::move(right) } {
                m_frequency = m_left->m_frequency + m_right->m_frequency;
            }
        };

        struct functor {
            bool operator()(smartNode const& first, smartNode const& second) const
            {
                return first->m_frequency > second->m_frequency;
            }
        };

        /*shared functions*/
        smartNode makeTree(std::deque<smartNode>& nodeData);
        void readFile(const std::string& filePath, std::string& fileContent);
        std::deque<smartNode> storeFreqTable(const Table& table);
        /*shared functions*/

        /*compressor related functions*/
        void setNameAndExten(const std::string& filePath, std::string& fileName, std::string& fileExten);
        void UpdateFreqTable(Table& freqTable, const std::string& fileContent);
        void encode(smartNode const &root, Cypher& key, std::vector<bool>& code);
        void createBinaryFile(const std::string& filePath,
                              const std::string& fileName,
                              const std::string& fileContent,
                              Cypher& key,
                              std::vector<bool>& code);
        void createKey(const std::string& filePath,
                       const Table& freqTable,
                       const std::string& fileName,
                       const std::string& fileExten);
        /*compressor related functions*/

        /*decompressor related functions*/
        void readKey(Table& freqTable,
                     std::string& fileExten,
                     const std::string keyPath,
                     std::string& fileContent);
        std::size_t decodedContentSize(const Table& freqTable);
        void decode(const std::string& filePath,
                    std::string& decodedContent,
                    smartNode root,
                    std::string& fileName,
                    std::string& fileContent);
        void createFile(const std::string& decodedContent,
                        const std::string& locToDecompress,
                        const std::string& fileName,
                        const std::string& fileExten);
        /*decompressor related functions*/
    }//end of inner namespace

    void compress(const std::string& filePath, const std::string& locToCreateKey, const std::string& locToCompress);
    void decompress(const std::string& filePath, const std::string& keyPath, const std::string& locToDecompress);
}//end of Huffman namespace

#endif

哈夫曼.cpp:

#include"Huffman.h"
#include<fstream>
#include<sstream>
#include<algorithm>
#include<cstdlib>


/*----------------SHARED_FUNCTIONS_START----------------*/
Huffman::inner::smartNode Huffman::inner::makeTree(std::deque<smartNode>& nodeData) {
    while (nodeData.size() > 1) {
        std::sort(nodeData.begin(), nodeData.end(), functor());
        smartNode leftSon{ std::move(nodeData.back()) };
        nodeData.pop_back();
        smartNode rightSon{ std::move(nodeData.back()) };
        nodeData.pop_back();
        smartNode parent = std::make_unique<node>(std::move(leftSon), std::move(rightSon));
        nodeData.emplace_back(std::move(parent));
    }//end of while loop
    return std::move(nodeData.front());
}

void Huffman::inner::readFile(const std::string& filePath, std::string& fileContent) {
    std::ifstream inFile(filePath, std::ios::binary);
    if (inFile.is_open()) {
        auto const start_pos{ inFile.tellg() };
        inFile.ignore(std::numeric_limits<std::streamsize>::max());
        std::streamsize char_count{ inFile.gcount() };
        inFile.seekg(start_pos);
        fileContent = std::string(static_cast<std::size_t>(char_count), '0');
        inFile.read(&fileContent[0], static_cast<std::streamsize> (fileContent.size()));
        inFile.close();
    }//end of if
    else {
        std::cout << "Unable to open file\n";
        std::exit(EXIT_FAILURE);
    }//end of else
}

std::deque<Huffman::inner::smartNode> Huffman::inner::storeFreqTable(const Table& table) {
    std::deque<smartNode> nodeData;
    for (const auto& index : table) {
        smartNode leaf = std::make_unique<node>();
        leaf->m_data = index.first;
        leaf->m_frequency = index.second;
        nodeData.emplace_back(std::move(leaf));
    }//end of for loop
    return nodeData;
}
/*-----------------SHARED_FUNCTIONS_END-----------------*/

/*-----------------COMPRESSOR_FUNCTIONS_START-----------------*/
void Huffman::inner::setNameAndExten(const std::string& filePath,
                                     std::string& fileName,
                                     std::string& fileExten) {
    std::size_t foundName{ filePath.find_last_of("/\\") };
    std::size_t foundExten{ filePath.find_last_of('.') };
    fileName = filePath.substr(foundName + 1, foundExten - foundName - 1);
    fileExten = filePath.substr(foundExten);
}

void Huffman::inner::UpdateFreqTable(Table& freqTable, const std::string& fileContent) {
    for (const auto& data : fileContent) {
        ++freqTable[data];
    }//end of for loop
}

void Huffman::inner::encode(smartNode const &root,
                            Cypher& key,
                            std::vector<bool>& code) {
    if (root->m_left != nullptr) {
        code.emplace_back(false);
        encode(std::move(root->m_left), key, code);
    }//end of if
    if (root->m_right != nullptr) {
        code.emplace_back(true);
        encode(std::move(root->m_right), key, code);
    }//end of if 
    if (root->m_data) key[root->m_data] = code;
    if (!code.empty()) code.pop_back();
}

void Huffman::inner::createBinaryFile(const std::string& filePath,
                                      const std::string& fileName,
                                      const std::string& fileContent,
                                      Cypher& key,
                                      std::vector<bool>& code) {
    int offSet{}; int tempBuff{}; int inBuff{};
    std::ofstream outFile(filePath + fileName + "Compressed.bin", std::ios::binary);
    if (outFile.is_open()) {
        for (const auto& data : fileContent) {
            tempBuff = data;
            code = key[static_cast<char>(tempBuff)];
            for (const auto& index : code) {
                inBuff |= index << (7 - offSet);
                ++offSet;
                if (offSet == 8) {
                    offSet = 0;
                    outFile.put(static_cast<char>(inBuff));
                    inBuff = 0;
                }//end of if
            }//end of for loop
        }//end of for loop
        outFile.close();
    }//end of if
    else {
        std::cout << "Unable to open file\n";
        std::exit(EXIT_FAILURE);
    }//end of else
}

void Huffman::inner::createKey(const std::string& filePath,
                               const Table& freqTable,
                               const std::string& fileName,
                               const std::string& fileExten) {
    std::ofstream outFile(filePath + fileName + "Key.bin", std::ios::binary);
    if (outFile.is_open()) {
        auto&& index{ freqTable.begin() };
        do {
            outFile.put(index->first);
            outFile.put(' ');
            outFile << std::to_string(index->second);
            ++index;
            if (index != freqTable.end()) outFile.put(' ');
        } while (index != freqTable.end());
        outFile << fileExten;
        outFile.close();
    }//end of if
    else {
        std::cout << "Unable to open file\n";
        std::exit(EXIT_FAILURE);
    }//end of else
}
/*------------------COMPRESSOR_FUNCTIONS_END------------------*/

/*-----------------DECOMPRESSOR_FUNCTIONS_START-----------------*/
void Huffman::inner::readKey(Table& freqTable,
                             std::string& fileExten,
                             const std::string keyPath,
                             std::string& fileContent) {
    char buffer{};
    std::string freq{};
    readFile(keyPath, fileContent);
    for (std::size_t index{}; index < fileContent.length(); ++index) {
        buffer = fileContent[index];
        index += 2;
        do {
            freq += fileContent[index];
            ++index;
        } while ((fileContent[index] != ' ') && (fileContent[index] != '.'));
        if (fileContent[index] == '.') {
            fileExten = fileContent.substr(index, (fileContent.length() - 1));
            index = fileContent.length();
        }//end of if
        else {
            freqTable[buffer] = static_cast<unsigned int>(std::stoi(freq));
            freq.clear();
        }//end of else
    }//end of for
    freqTable[buffer] = static_cast<unsigned int>(std::stoi(freq));
    fileContent.clear();
    fileContent.shrink_to_fit();
}

std::size_t Huffman::inner::decodedContentSize(const Table& freqTable) {
    std::size_t size{};
    for (const auto& index : freqTable) size += index.second;                   
    return size;
}

void Huffman::inner::decode(const std::string& filePath,
                            std::string& decodedContent,
                            smartNode root,
                            std::string& fileName,
                            std::string& fileContent) {
    node* temp = root.get();
    int offSet{}; int inBuff{};
    std::size_t foundName{ filePath.find_last_of("/\\") };
    fileName = filePath.substr(foundName + 1, filePath.find_last_of('C') - foundName - 1);      
    readFile(filePath, fileContent);
    for (const auto& data : fileContent) {
        inBuff = data;
        while (offSet < 8) {
            if (inBuff & (1 << (7 - offSet))) temp = temp->m_right.get();
            else                              temp = temp->m_left.get();
            if (temp->m_data) {
                decodedContent += temp->m_data;
                temp = root.get();
            }//end of if 
            ++offSet;
        }//end of while
        offSet = 0;
    }//end of for
}

void Huffman::inner::createFile(const std::string& decodedContent,
                                const std::string& locToDecompress,
                                const std::string& fileName,
                                const std::string& fileExten) {
    std::ofstream outFile(locToDecompress + fileName + fileExten, std::ios::binary);
    if (outFile.is_open()) {
        outFile.write(&decodedContent[0], static_cast<std::streamsize>(decodedContent.size()));
        outFile.close();
    }//end of if
    else {
        std::cout << "Unable to open file\n";
        std::exit(EXIT_FAILURE);
    }//end of else
}
/*------------------DECOMPRESSOR_FUNCTIONS_END------------------*/

void Huffman::compress(const std::string& filePath,
                       const std::string& locToCreateKey,
                       const std::string& locToCompress) {
    std::string                        fileName;
    std::string                        fileExten;
    Huffman::inner::setNameAndExten(filePath, fileName, fileExten);

    std::string                        fileContent;
    Huffman::inner::readFile(filePath, fileContent);

    Huffman::inner::Table              freqTable;
    Huffman::inner::UpdateFreqTable(freqTable, fileContent);

    Huffman::inner::smartNode root = Huffman::inner::makeTree(Huffman::inner::storeFreqTable(freqTable));

    Huffman::inner::Cypher             key;
    std::vector<bool>                  code;
    encode(root, key, code);
    Huffman::inner::createBinaryFile(locToCompress, fileName, fileContent, key, code);
    Huffman::inner::createKey(locToCreateKey, freqTable, fileName, fileExten);
}

void Huffman::decompress(const std::string& filePath,
                         const std::string& keyPath,
                         const std::string& locToDecompress) {
    Huffman::inner::Table       freqTable;
    std::string                 fileExten;
    std::string                 fileContent;
    Huffman::inner::readKey(freqTable, fileExten, keyPath, fileContent);

    Huffman::inner::smartNode root = Huffman::inner::makeTree(Huffman::inner::storeFreqTable(freqTable));

    std::string                 fileName;
    std::string                 decodedContent;
    decodedContent.reserve(Huffman::inner::decodedContentSize(freqTable));
    decode(filePath, decodedContent, std::move(root), fileName, fileContent);
    Huffman::inner::createFile(decodedContent, locToDecompress, fileName, fileExten);
}

最佳答案

编辑:固定功能:

decode(const std::string& filePath,
                            std::string& decodedContent,
                            smartNode root,
                            std::string& fileName,
                            std::string& fileContent) {
    node* temp = root.get();
    int offSet{}; int inBuff{};
    std::size_t foundName{ filePath.find_last_of("/\\") };
    fileName = filePath.substr(foundName + 1, filePath.find_last_of('C') - foundName - 1);      
    readFile(filePath, fileContent);
    for (const auto& data : fileContent) {
        inBuff = data;
        while (offSet < 8) {
            if (inBuff & (1 << (7 - offSet))) temp = temp->m_right.get();
            else                              temp = temp->m_left.get();
            if (temp->m_data) {
                decodedContent += temp->m_data;
                temp = root.get();
            }//end of if 
            ++offSet;
        }//end of while
        offSet = 0;
    }//end of for
}

关于c++ - 哈夫曼压缩器/解压器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43912472/

相关文章:

C++如何在字符后获取子字符串?

c++ - '*' 标记之前的预期构造函数、析构函数或类型转换

c - 使用 malloc 时触发异常

java - 使用普通对象指针(OOP)在32位模式下但在64GB RAM上运行Java应用程序

c++ - 多线程和多进程应用程序的锁定机制有什么区别?

c++ - 使用 openMP 的线程安全随机数生成器

c++ - 在 NULL 指针上调用 delete 或 delete[]

c++ - 基类 C++14 模板函数在 Mac OS 上的 clang 中不可见(递归模板)

c++ - 连接三个不同的对象

C++显示在txt文件中找到的唯一日期及其相关价格