新人向:LZW压缩算法(C语言)

引言

今天给大家介绍一种常见的压缩算法——LZW压缩算法。
LZW算法的基本原理为:通过构建一个字典,在该字典中用不同的编码去表示不同字符串,从而达到节省空间的目的。
例如:我有一个字符串"abcdabcdabcd",共占12字节,如果我有一个字典,里面用1来代表"abcd",那么只需令编码表(我们称之为key[])中存放三个1即可,倘若我们同样使用char类型,原来的字符串就由12字节压缩至3字节。
下面我将为大家介绍压缩和解压缩的具体过程。

压缩

压缩是一个一边推导字典,一边记录key[]的过程。
一开始字典只有基础编码,也就是压缩包中有可能出现的字符,为了方便起见,我们规定其范围为A~Z,值从1~26。
此外,介绍一下命名的含义:
i: 当前检索的字符
next: 下一个需要检索的字符
key[]:获得的编码表,也就是压缩后的文件
压缩过程:

  1. 查询字典至当前字符串(称之为current)中无匹配项
  2. 将current插入字典(此处是一个不断更新字典的过程,便于下次再出现current时,能够对其快速编码)
  3. 设current有n位,取n-1位对应字典中编码,插入key[]
  4. 继续检索至字符串结束

例如,对于字符串"TOTOB"

  1. i对应T,next对应O,此时字典中有T,继续查询字典中是否有TO,未查询到,则把T对应编码写入key[]中,TO插入字典;
  2. i对应O,next对应T,此时字典中有O,继续查询字典中是否有OB,未查询到,则把O对应编码写入key[]中,OB插入字典;
  3. i对应T,next对应O,此时字典中有T,继续查询字典中是否有TO,查询到则继续查询是否有TOB,未查询到,把TO对应编码写入key[]中,TOB插入字典;
  4. 由于TO已存入key[]中,本次i直接对应B,next无对应,此时字典中有B,把B对应编码写入key[]中,结束压缩。

解压缩

好了,现在我有这个字典(Dictionary)和编码(key[]),那么是不是每次我都要存储字典来供查找呢?
LZW算法的神奇之处在于:解压缩方不需要字典也能够对其进行解压,能够利用已知编码和基础编码一边翻译,一边逆推导出字典。
同样介绍命名含义:
prev: 当前检索字符的前一字符串
output: 当前输出
推导:
现在我们只有key[],如何推导字典呢?
不难发现,字典由每一次的prev加上output的第一个字母构成。
还是拿"TOTOB"举例子,插入字典的字符串为字典中能找到的最长字符串加上一个后面的字符,当我们插入TOB时,已有的是TO(即prev),后一字符为B(output的第一位)
解压缩过程

  1. 将上一次的输出output作为prev
  2. 读出并打印当前output
  3. 将当前output的第一个字母和prev连接并插入字典

代码

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//定义字典结构
typedef struct
{
    char **sequence;//实际指向字符串的二级指针
    int *code;      //给字符串编号
    int size;       //当前长度
    int max_size;   //最大长度
}Dictionary;
//压缩后的编码
int key[1000];
//对应下标
int count = 0;
//向key中插入编码
void insert_key(int code)
{
    key[count++] = code;
}
//向字典中插入字符串str
void insert_sequence(Dictionary *dict, char *str)
{
    int i = dict->size;
    dict->sequence[i] = (char*)malloc(sizeof(char) * strlen(str) + 1);
    dict->code[i] = i;
    dict->size++;
    strcpy(dict->sequence[i], str);
}
//初始化字典
void init_dictionary(Dictionary *dict, int max_size)
{
    dict->max_size = max_size;
    dict->size = 0;
    dict->sequence = (char**)malloc(sizeof(char*) * max_size);
    dict->code = (int*)malloc(sizeof(int) * max_size);
    //插入哑值(任意)
    insert_sequence(dict, (char*)"#");
    //插入A到D字母(只有)
    char letter[2] = "A";
    for (int i = 0; i < 26; i++)
    {
        insert_sequence(dict, letter);
        letter[0]++;
    }
}
//已知字符串,查询字符串是否存在,存在返回编码(压缩用)
int get_code(Dictionary *dict, char *str)
{
    for (int i = 0; i < dict->size; i++)
    {
        if (strcmp(dict->sequence[i], str) == 0)
        {
            return dict->code[i];
        }
    }
    return -1;
}
//已知编码,获取字符串(解压用)
char *get_sequence(Dictionary *dict, int code)
{
    return dict->sequence[code];
}
//打印字典
void print_dictionary(Dictionary *dict)
{
    printf("====================\n");
    printf(" Code       Sequence\n");
    printf("====================\n");
    for (int i = 0; i < dict->size; i++)
    {
        printf("%5d%7c%s\n", i, ' ', dict->sequence[i]);
    }
    printf("====================\n");
}

void print_key()
{
    for (int i = 0; i < count; i++)
    {
        printf("%d ", key[i]);
    }
    putchar('\n');
}
//压缩编码
void lzw_encode(char *text, Dictionary *dict)
{
    char current[1000];
    char next;
    int i = 0;
    while (i < strlen(text))
    {
        //先把第i个字符存入(对于后续循环,该处也有一个清空current的作用)
        sprintf(current, "%c", text[i]);
        next = text[i+1];
        //反复查询并添加current,直到current为最新,退出循环
        while (get_code(dict, current) != -1)
        {
            //  i   current     next
            //  T         T      O  
            //  O        TO      B
            //  B       TOB      C
            sprintf(current, "%s%c", current, next);
            i++;
            next = text[i+1];
        }
        //编码并存入数组key
        //
        //无效sequence无需插入
        if (i < strlen(text))
            insert_sequence(dict, current);
        current[strlen(current) - 1] = '\0';
        insert_key(get_code(dict, current));
    }
}
//解压编码
void lzw_decode(Dictionary *dict)
{
    int code;
    char prev[1000];
    char *output;
    output = get_sequence(dict, key[0]);
    //第一项无前一项,单独输出   
    printf("%s", output);
    for (int i = 1; i < count; i++)
    {
        code = key[i];
        strcpy(prev, output);
        output = get_sequence(dict, code);
        printf("%s", output);
        //此处把prev作为一个临时字符串(反正上面strcpy时会更新)
        sprintf(prev, "%s%c", prev, output[0]);
        insert_sequence(dict, prev);
    }
}

int main(int argc, char *argv[])
{
    Dictionary dict;
    //char text[] = "TOTOB";
    char text[] = "TOBEORNOTTOBEORTOBEORNOT";
    init_dictionary(&dict, 100);
    lzw_encode(text, &dict);
    print_dictionary(&dict);
    printf("Text : \n%s\n", text);
    printf("Code : \n");
    print_key();
    //重新初始化用于解码
    init_dictionary(&dict, 100);
    printf("Decode : \n");
    lzw_decode(&dict);
    return 0;
}

参考

LZW压缩算法1

LZW压缩算法2

  • 9
    点赞
  • 32
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
LZW(Lempel-Ziv-Welch)压缩算法是一种常用的无损数据压缩算法,它可以将输入的数据流进行压缩,并在需要时进行解压缩以还原原始数据。VHDL(VHSIC Hardware Description Language)是一种硬件描述语言,常用于数字电路设计和硬件描述。 LZW压缩算法的核心思想是利用字典来存储已经出现的序列,然后将序列替换为对应的索引值。在压缩过程中,算法会不断扩展字典,以适应新出现的序列。而在解压缩过程中,算法会根据索引值从字典中查找对应的序列,并将其还原。 在VHDL语言中实现LZW压缩算法,可以采用状态机的方式描述算法的不同状态和操作。其中,需要实现的核心功能包括: 1. 初始化字典:在算法开始时,需要初始化字典,包括预定义的单字符序列和初始的索引值。 2. 压缩过程:读取输入数据流,并根据当前读取的序列在字典中查找对应的索引值。如果查找成功,则继续读取下一个字符并与当前序列拼接,继续在字典中查找;如果查找失败,则将当前序列的索引值输出,并将当前序列添加到字典中,并将下一个字符作为新的序列开始。 3. 解压缩过程:读取压缩后的数据流,并根据索引值在字典中查找对应的序列。然后将序列输出,并将序列添加到字典中,并将下一个索引值作为新的序列开始。 需要注意的是,在VHDL语言中实现LZW压缩算法需要考虑到硬件资源和性能的限制,需要合理设计状态机和字典存储结构,以达到高效的压缩和解压缩效果。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值