在C中压缩ASCII字符串

时间:2009-07-08 10:09:04

标签: c compression

我有一些C代码将ASCII字符串存储在内存中,作为四字节长度后跟字符串。字符串长度在10-250字节范围内。

为了减少占用,我想动态地单独压缩每个字符串,仍然存储(压缩字符串的)长度,然后是压缩字符串。

我不想在比单个字符串更大的范围内进行压缩,因为任何字符串都可以随时读/写。

可以使用哪些库/算法?

感谢您的帮助。 NickB

6 个答案:

答案 0 :(得分:14)

ZLib总是在为您服务 - 当字符串包含不可压缩的数据时,它的开销非常小,它相对快速,免费且可以轻松集成到C和C ++程序中。

答案 1 :(得分:10)

对于短字符串,大多数压缩算法都不能很好地工作。 以下是一些压缩算法,旨在压缩简短的英文文本字符串。 虽然他们可以处理明文字符串中的任意字节, 这样的字节通常使“压缩”数据比明文更长。 因此,压缩器不会更改存储“不可压缩”数据并在此类数据上设置“文字”标记(如Steve Jessop所建议的那样)。

  • “base 40 encoding”:最大压缩3:2
  • “Zork标准信息交换规范”(ZSCII):最大压缩3:2
  • byte pair compression:最大压缩比例为2:1
  • 在所有字符串中共享的静态Huffman表(由cygil建议)。
    • 理想情况下,由您所有实际数据的确切字符频率组成。
    • Varicode:最大压缩2:1
  • PalmDoc compression(字节对压缩+ LZ77的简单变体)。

答案 2 :(得分:4)

我不确定zlib或LZW压缩方法在单独压缩小于250字节的短字符串的情况下是否能正常工作。两者通常都需要在看到显着的压缩增益之前创建一个相当大的字典。

也许简单的霍夫曼编码使用固定的编码树,或者在所有字符串实例之间共享?另外,你有没有看过用于在80年代内存受限的微型计算机上压缩短字符串的ZSCII编码?

link text

答案 3 :(得分:3)

Zlib绝对是你的朋友,但是请确保执行一些测试来检测压缩开始有利的平均字符串长度,因为压缩头的开销很小。

例如,您可能会发现在20个字符以下,压缩字符串实际上更大,因此只压缩更长的字符串。

答案 4 :(得分:3)

为什么在字符串长度为10-250字节时使用4字节长度,使用1字节长度,每个字符串只能节省3个字节。

数据是否只是文本,即0-9 A-z或某些子集?如果重新编码则使用该子集并为每个字符保存几位。

现在看看Huffman编码部分和lempel-zev部分的http://gnosis.cx/publish/programming/compression_primer.html

这应该让你开始。

答案 5 :(得分:1)

当使用这样的多个字符串时,可以通过将它们与\0 s(1个字节)连接在一起并使用查找函数来避免每个字符串的指针开销(每个字符串4或8个字节)。

#include <stdio.h>

static const char strings[]="hello\0world\0test";

char * nthstring(const char *s, unsigned n){
    while(n--)
        while(*s++)
        ;
    return s;
}
int main(void) {
    printf("%s\n",nthstring(strings,1));
    return 0;
}

但是,如果字符串长度小于UCHAR_MAX,则可以使用零字节占位符来优化查找,以便存储长度(在开头加1个额外值)这只需要额外增加1个数据字节,但可以节省大量条件跳转和查找函数中的增量。

#include <stdio.h>
/* each "string" is prefixed with its octal length */
static const char lenstrings[]="\05hello\05world\04test";

char * ithstring(const char *s, unsigned n){
    while(n--){
        s+=*s+1;
    }
    return s;
}
int main(void) {
    char *s=ithstring(lenstrings,1);
    /* use the length because we don't have terminating \0 */
    printf ("%.*s",(unsigned char)*s,s+1);
    //write(1,s+1,(unsigned char)*s); //POSIX variation via <unistd.h>
    return 0;
}

对于这两种变体,最好先保留最常用的字符串;但是,第二种方法允许您使用压缩数据(选择最适合您数据的数据 - David Cary's answer有一个可行解决方案列表),只要您将长度分隔符调整为压缩长度。

注意:要从标准压缩器中获得最大压缩,您可能希望将其标头的长度字段修改为unsigned char(或unsigned short如果字符串长度超过256但不是65536字节)因为他们中的大多数将尝试支持大文件的压缩(这可以节省每串3-7个字节)