zoukankan      html  css  js  c++  java
  • C++实现的Huffman压缩解压缩程序及相应程序框架的设计

    前面用python实现了基于256个字符huffman及范式huffman压缩解压缩程序。Python确实适合快速实现算法,包括程序的框架设计的实现。

    但是无奈虽然尝试优化但是速度仍然不尽如意,包括无法实现inline,以及动态语言的特性决定这种强调速度,处理大数据量的应用程序显然不适合用python实现。

    正好看了关于基于模版的算法库设计的一些皮毛,以及以前看CGAL库的学到一些方法。这里尝试用C++写一个压缩解压缩的框架程序,当前实现的框架还很幼稚,

    是考虑到能够实现Huffman,范式huffman的基于256个字符,以及基于word单词的压缩与解压缩。也就是能够实现

    1. 基于字符编码的huffman压缩,解压缩

    2.基于字符编码的范式huffman压缩,解压缩

    3.基于单词编码的huffman压缩,解压缩

    4.基于单词编码的范式hufman压缩,解压缩

    这些都是基于频率的(字符频率,单词频率)的压缩算法。

    TODO

    考虑系统能方便的加入其它算法

    如LZ77(不是基于频率的压缩)等。。。。

    OK,目前仅仅初步搭起了框架,实现了上面提到的1,基于字符编码的huffman压缩,解压缩。

    当前程序在http://golden-huffman.googlecode.com/svn/trunk/glzip_c++/

    同时框架尽可能考虑到后序加入不同算法的方便性,不同算法的共同与不同之处,层次关系,相同部分的复用,避免重复代码。程序里面写了一个Buffer类,用于提供基于缓冲的的读写

    byte操作,以减少fread,fwrite的调用次数。实践证明能够提高效率,相比每次fread,fwrite 1个byte不带缓冲,同时使得代码清晰。

    image

    还没有尝试算法的优化,比如当前解码的时候一个unsigend int 转换成相应的bit,采用最笨的动态计算。

    可以考虑查表法优化。

    不过当前的速度可python实现的比已经快多了。

    在我的1G内存虚拟机上跑,

    压缩,然后解压缩一个24M的文本,总共用时大概7-8秒。用GCC编译器优化选项-O2能够达到3-4秒完成。

    程序的框架上,首先考虑定义两个类

    Compressor,Decompressor

    image

    image

    提供压缩,解压缩的流程框架。对于用户而言

    Compressor<> compressor(infile_name, outfile_name);

    compressor.compress();

    即完成压缩工作。解压缩类似。

    template<
    template<typename> class _Encoder = HuffEncoder,
    typename _KeyType = unsigned char
    >
    class Compressor {
    public:
    Compressor(const std::string& infile_name, std::string& outfile_name)
    : encoder_(infile_name, outfile_name) {}
    /**The overall process of compressing,compressing framework*/
    void compress() {
    encoder_.caculate_frequency();
    encoder_.gen_encode();
    //-------------------------------write the compressed file
    encoder_.write_encode_info();
    encoder_.encode_file();
      }
    private:
    _Encoder<_KeyType> encoder_;  //using enocder_ right now can be HuffEncoder or CanonicalEncoder 
    };
    template<
    template<typename> class _Decoder = HuffDecoder,
    typename _KeyType = unsigned char
    >
    class Decompressor {
    public:
    Decompressor(const std::string& infile_name, std::string& outfile_name)
    : decoder_(infile_name, outfile_name) {}
    /**The overall process of decompressing, decompressing framework*/
    void decompress() {
    //-----------------------------read header--------
    decoder_.get_encode_info();
    //-----------------------------read file content---
    decoder_.decode_file();
    }
    private:
    _Decoder<_KeyType> decoder_;
    };

    采用复合的设计方法,Compressor类使用encoder_完成压缩工作。Decompressor类使用decoder_完成解压缩的工作。

    模版参数上,template<typename> class _Encoder = HuffEncoder, 标明可以采用HuffEncoder完成基于传统huffman的压缩,

    同时我们可以定义CanonicalEncoder类,令template<typename> class _Encoder = CanonicalEncoder就可以方便的使得

    Compressor类使用基于范式huffman的encoder_完成压缩工作。

    而 typename _KeyType = unsigned char标明默认Compressor是采用基于字符(256个字符)作为key的,进行编码解码。

    那么后面我们加入基于单词的编码方法,就可以采用使得typename _KeyType = std::string即可,或者也可以用char*这里暂时都按照单词用string存储考虑。

    压缩的过程就是

    计算字符(或单词)频率

    进行编码,利用构造的huffman tree

    写编码信息到输出文件

    编码输入文件输出到输出文件

    在这个过程中我们需要保存

    1.得到频率hash表          字符(或单词) –> 频率   

    2.得到的编码hash表      字符(或单词) –>  编码

    另外我们需要处理输入输出,需要FILE* infile_,FILE* outfile_所有这些我们作为encoder所拥有的变量。

    image

         HuffEncoder is a Encoder,继承Encoder,同样的以后加入的CanonicalEnocder is also a Encoder也会继承Encoder.

         Encoder所提供的非虚函数接口,caculate_frequency()和encode_file()等,标明这些由Encoder类提供实现,也即对于

    传统Huffman还是范式Huffman来说,这些操作的实现是相同的,复用基类的实现。

    但是其它的虚函数则要他们提供各自不同的实现。

    另外当前的处理,对于基于字符编码的情况,_KeyType = unsigned char,

    使用的所谓哈希表并不是STL里面的哈希表,而就是一个数组,例如 frequency_map_对于基于字符编码的情况,它就是 long long (&)[256]类型的数组。

    这样利用了unsigned char 不超过256个byte,并且转int值之后正好当成一个hash函数,用STL的hash就小题大作了,速度也慢多了。

    但是考虑兼容以后的string,基于单词编码的情况就得用STL hash了,或者你自己写hash。

    所以这里采用了traits手法,根据_KeyType的不同,选择不同类型的 Hash容器类型,并且在函数中也会根据不同的情况,分派到不同的执行函数char –> char_tag

    string –> string_tag。(当然以后也可以考虑给long long (&)[256]类型的数组这种简单的hash,加上与STL hash相同的接口,从而是代码更加同一,避免分配函数,不过目前感觉那样太费时间了。

    下面是根据不同的_KeyType,给出不同的类型设置。

    //基于字符情况的encoder<unsigned char>.caculate_frequencey(),以后实现的基于单词情况的则会调用encoder<std::string>.do_caculate_frequencey(string_tag)

    00034   typedef typename TypeTraits<_KeyType>::FrequencyHashMap        FrequencyHashMap;
    00035   typedef typename TypeTraits<_KeyType>::EncodeHashMap           EncodeHashMap;
    00036   typedef typename TypeTraits<_KeyType>::type_catergory type_catergory;
    00037 public:
    00047   void caculate_frequency() {
    00048     do_caculate_frequency(type_catergory());
    00049   }
    00050   
    00080   void do_caculate_frequency(char_tag) {
    00081     Buffer reader(infile_);
    00082     unsigned char key;
    00083     while(reader.read_byte(key))
    00084       frequency_map_[key] += 1;
    00085     //std::cout << "Finished caculating frequency\n"; 
    00086   }
    //---------------------------------------------------------------------------
    00021 struct normal_tag {};
    00022 struct char_tag: public normal_tag {};
    00023 struct string_tag: public normal_tag {};
    00024
    00025 struct encode_hufftree {};
    00026 struct decode_hufftree {};
    00027 //----------------------------------------------------------------------------
    00028
    00029 //---TypeTraits,from here we can find the HashMap type 
    00030 template <typename _KeyType>
    00031 class TypeTraits {
    00032 public:
    00033   typedef normal_tag  type_catergory;
    00034   typedef std::tr1::unordered_map<_KeyType, size_t> HashMap;
    00035 };
    00036 //---special TypeTraits for unsigned char
    00037 template<>
    00038 class TypeTraits<unsigned char> {
    00039 public:
    00040   typedef char_tag type_catergory;
    00041   typedef long long count[256];
    00042   typedef count     FrequencyHashMap;
    00043   typedef std::vector<std::string>    EncodeHashMap;
    00044 };
    00045 //---special TypeTraits for std::string
    00046 template<>
    00047 class TypeTraits<std::string> {
    00048 public:
    00049   typedef string_tag type_catergory;
    00050   typedef std::tr1::unordered_map<std::string, size_t>        FrequencyHashMap;
    00051   typedef std::tr1::unordered_map<std::string, std::string>   EncodeHashMap;
     
     
     
    HuffEncoder,利用复合设计模式,采用HuffTree来帮助实现编码。
    image 
     
    为了Huffman压缩过程和解压缩过程,我利用模版类的特化设计了两个不同的HuffTree分别用于压缩过程,而解压缩过程。它们继承一个HuffTreeBase,提供共有的数据root_和操作如delete_tree().
    这里感觉最不爽的还是GCC采用的不认模版父类的名称必须显示指明或者用this->,据说VC8就不需要显示指明或者加this,那多好啊,感觉真是没必要,麻烦的要命。尤其是刚开始你觉得只要一个
    类,后来你要写一个类似但是不同的类,觉得可以提出一个基类的时候,你发现你把共同的代码提出去了,剩下的代码原来可以用的现在却要加很多this,麻烦死了啊还不如不复用,直接复制快呢。
    image 
     
    image 
  • 相关阅读:
    SDN课程阅读作业(2)
    2019 SDN上机第5次作业
    linux 最常用命令
    tomcat 部署 React 项目后,浏览器刷新报404问题
    Java8 ~ 特性
    React ~ 小结
    ES6 map与filter
    Idea 快捷键
    实现div可以调整高度(div实现resize)
    linux常用命令(4)
  • 原文地址:https://www.cnblogs.com/rocketfan/p/1579824.html
Copyright © 2011-2022 走看看