美文网首页
哈夫曼编码加密和解密【C++】

哈夫曼编码加密和解密【C++】

作者: Jfeng666 | 来源:发表于2019-05-10 18:35 被阅读0次

    环境是Visual Studio 2017 x64 本地

    第一行是防C4996警报妨碍scanf等函数的正常编译使用

    =v=还没做的同学可以参考一下

    加密代码思路

    12source1
    逐字符读入文章,记录读入的不同种字符及数量,
    分别作为哈夫曼树的结点和权值。
    构造哈夫曼树
    用根节点遍历哈夫曼树
    得到各字符的编码,并输出到12code
    再次读入文章,并根据字符将对应编码输出在12encrypt

    解密代码思路

    读取12code构造一颗哈夫曼树
    读入12encrypt边读边遍历,直到遇到叶子结点输出
    将结果输出到12source2

    加密Encrypt.cpp

    #pragma warning(disable:4996)
    
    //#include <bits/stdc++.h>
    
    #include <iostream>
    #include <cstdio>
    #include <string>
    #include <cctype>
    using namespace std;
    
    #define MaxSize 10000
    
    struct HTnode { //结点符号,权值,父结点,左右子结点
        char ch;
        int weight;
        HTnode *pa;
        HTnode *lc, *rc;
    };
    
    FILE *in, *out;
    HTnode* s[MaxSize];
    string code[MaxSize], table;
    int n;
    //table装读入的不同的字符,code装table字符对应位置的哈夫曼加密编码
    //结点的权值也就是出现的数量,n装字符数量,s装所有结点的指针
    
    void InitHTnode(HTnode *&h) {
        h = (HTnode *)malloc(sizeof(HTnode));
        h->pa = h->lc = h->rc = NULL;
        h->ch = h->weight = 0;
    }//初始化结点,防止ch越界
    
    void Init() {
        n = 0;
        int k, p;
        string st = "";
        in = fopen("12source1.txt", "r+");
        if (in == NULL) {
            puts("读入失败12source1.txt");
            exit(1);
        } //对读入输出异常的处理,后面文件流打开都会控制
        while (~(k = fgetc(in))) { //逐字符读入
            p = st.find((char)k); //查是否有重复
            if (p == -1) {
                st = st + (char)k;
                //无重复,加入table,加入叶子结点
                InitHTnode(s[n]);
                s[n]->lc = s[n]->rc = s[n]->pa = NULL;
                s[n]->ch = (char)k;
                s[n]->weight = 1;
                n++;
            }
            else {
                s[p]->weight++;
            }
        }
        table = st;
        if (n == 1) { //处理只有一种字符的情况
            s[1] = s[0];
            InitHTnode(s[0]);
            s[0]->lc = s[1];
            s[1]->pa = s[0];
        }
        fclose(in); //确认文件流打开,用完就必须关闭
    }
    
    void Cal() {
        int i, k;
        HTnode  *mn1, *mn2; //分别装最小最大值
        for (k = n; k < n * 2 - 1; k++)
        { //k装父亲结点
            mn1 = mn2 = NULL;
            for (i = 0; i < k; i++)
                if (!s[i]->pa) //找树根,并将权值排序
                {
                    if (!mn1 || s[i]->weight < mn1->weight) {
                        mn2 = mn1;
                        mn1 = s[i];
                    }
                    else if (!mn2 || s[i]->weight < mn2->weight) {
                        mn2 = s[i];
                    }
                }
            InitHTnode(s[k]); //将最小权值两棵树合并,生成新的树
            s[k]->weight = mn1->weight + mn2->weight;
            mn1->pa = s[k];
            mn2->pa = s[k];
            s[k]->lc = mn1;
            s[k]->rc = mn2;
        }
    }
    
    void find(HTnode *h, string s)
    {
        if (h->lc) find(h->lc, s + "0");
        if (h->rc) find(h->rc, s + "1");
        if (!h->lc && !h->rc && h->ch > 0 && h->ch < 256) {
            code[table.find(h->ch)] = s;
            if (isgraph(h->ch))
                fprintf(out, "%c", h->ch);
            else
                fprintf(out, "%d#", (int)h->ch); 
            //如果是非打印字符,输出它的ASCII码,否则输出
            fprintf(out, " %s\n", s.c_str());
        }
    }
    
    void DispCode() {
        int i;
        out = fopen("12code.txt", "w+");
        if (out == NULL) {
            puts("输出流获取失败");
            exit(1);
        }
        for (i = 0; i < n * 2 - 1; i++) //找树根开始遍历
            if (!s[i]->pa) {
                string st = "";
                find(s[i], st);
                break;
            }
        fclose(out);
    }
    
    void Encrypt() {
        in = out = NULL;
        in = fopen("12source1.txt", "r+");
        if (in == NULL) {
            puts("读入失败12source1.txt");
            exit(1);
        }
        out = fopen("12encrypt.txt", "w+");
        if (out == NULL) {
            fclose(in);
            puts("输出流获取失败");
            exit(1);
        }
        int k;
        while (~(k = fgetc(in))) {
            fprintf(out, "%s", code[table.find(k)].c_str());
        } //读入文章并根据出现的字符输出编码
        fclose(in);
        fclose(out);
    }
    
    
    int main()
    {
        Init();
        Cal();
        puts("输出哈夫曼书每个字符的编码到文件12code.txt");
        DispCode();
        puts("输出加密编码结果");
        Encrypt();
        return 0;
    }
    

    解密Decrypt.cpp

    #pragma warning(disable:4996)
    
    //#include <bits/stdc++.h>
    
    #include <iostream>
    #include <cstdio>
    #include <string>
    #include <cctype>
    using namespace std;
    
    struct BTnode {
        char data;
        BTnode *lc, *rc;
    } *root;
    
    FILE *in, *out;
    
    void InitBTnode(BTnode *&b) {
        b = (BTnode*)malloc(sizeof(BTnode));
        b->lc = b->rc = NULL;
        b->data = 0;
    } //初始化结点,data为0说明不是叶子结点
    
    void Init()
    {
        char ch[10], cd[300], *p;
        InitBTnode(root);
        BTnode *pt;
        int k;
        in = fopen("12code.txt","r+");
        if (in == NULL) {
            puts("读入12code.txt失败");
            exit(1);
        }
        while (~fscanf(in, "%s%s", ch, cd)) {
            //读取符号或者符号编码,还有哈夫曼编码
            if (ch[1]) {
                p = ch;
                k = 0;
                while (*p != '#') {
                    k = k * 10 + *p - '0';
                    p++;
                }
            }
            else k = ch[0];
            //至此,符号读取完毕,编码存于k
            //cout << ch << " " << k << " " << cd << endl;
            p = cd;
            pt = root;
            //从根节点开始,按着符号编码进行遍历
            while (*p) {
                if (*p == '1') {
                    if (!pt->rc)
                        InitBTnode(pt->rc);
                    pt = pt->rc;
                }//没有遍历时如果将遍历的结点为空,则新增
                else {
                    if (!pt->lc)
                        InitBTnode(pt->lc);
                    pt = pt->lc;
                }
                p++;
            }
            pt->data = (char)k;
        }
        fclose(in);
        in = NULL;
    }
    
    void Match() {
        in = fopen("12encrypt.txt", "r+");
        if (in == NULL) {
            puts("读入12encrypt.txt失败");
            exit(1);
        }
        out = fopen("12source2.txt", "w");
        if (out == NULL) {
            fclose(in);
            puts("文件输出流获取失败12source2.txt");
            exit(1);
        }
        int k = fgetc(in); //读取第一个编码
        BTnode *pt=NULL;
        while (~k) {
            pt = root; //开始第一个字符的读入
            while (!pt->data)
            {
                if (k == '1')
                    pt = pt->rc;
                else
                    pt = pt->lc;
                k = fgetc(in);
            } //直到遍历到的结点为叶子结点,开始输出
            fprintf(out, "%c", pt->data);
            //cout << (char)pt->data;
        }
        fclose(out);
        fclose(in);
    }
    
    int main()
    {
        Init();
        Match();
        return 0;
    }
    

    相关文章

      网友评论

          本文标题:哈夫曼编码加密和解密【C++】

          本文链接:https://www.haomeiwen.com/subject/anqzoqtx.html