用于在没有外部库的情况下在 C 中传输的整数数据压缩

Integer data compression for transfer in C without external libraries

我用谷歌搜索并在这里搜索了很多都没有合适的解决方案。标题可能有点奇怪或不完全准确,但让我解释一下: 我的物联网设备每秒收集大量数据,我可以将这些数据表示为整数列表。这是一行传感器读取的示例(顺便说一句,零并不总是 0):

230982 0 4294753011 -9 4294198951 -1 4294225518 0 0 0 524789 0 934585 0 4 0 0 0 0

触发时,我想将整个 table(到那时为止的所有行)发送到我的计算机。我可以将它字符串化并连接所有内容,但想知道是否有更有效的 encoding/compression 来减少字节数,无论是在存储在 RAM/flash 中还是为了减少传输量。理想情况下,这可以通过集成功能来实现,即没有外部压缩库。 encoding/compression我不是很厉害,希望大家指点一下

最简单的解决方案是以二进制形式简单地转储数据。它可能比字符串形式更小或更大,具体取决于您的数据,但您不必在设备上进行任何数据处理。

如果您的大部分数据都很小,您可以使用可变长度数据编码进行序列化。有好几种,但是CBOR比较简单

如果您的数据变化很小,您可以只发送第一行作为绝对值,其余行作为前一行的增量。这将导致许多小数字,这在前面提到的编码系统中通常更有效。

我不会在没有任何经验和外部库的情况下尝试实现任何通用压缩算法,除非您绝对需要它。找到合适的算法来充分压缩您的数据并合理使用资源可能很耗时。

Zlib/Zstd 库更适合进行通用压缩。如果我假设您不想使用任何第三方库,这里是一些简单压缩方法的手工编码版本,它节省了输入字符串的一半字节。

基本思路很简单。您的字符串最多有 16 个不同的字符,可以映射到 4 位而不是典型的 8 位。请参阅以下假设。您也可以尝试 base16base64base128 编码,但这是最简单的。 假设:

  1. 首先,您会将所有数字转换为十进制格式的字符串。
  2. 该字符串将不包含除 0,1,2,3,4,5,6,7,8,9,+,-,.spacecomma.
  3. 之外的任何其他字符

============================================= ===============================

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

static inline char map(char c)
{
        switch(c) {
        case ' ' : return ('/' - '*');
        case '[=10=]': return 0;
        default  : return c - '*';
        }
        return 0;
}

static inline char revmap(char c)
{
        switch(c) {
        case '[=10=]'     : return 0;
        case '/' - '*': return ' ';
        default       : return c + '*';
        }
        return 0;
}

char *compress(const char *s, int len)
{
        int i, j;
        char *compr = malloc((len+1)/2 + 1);

        j = 0;
        for (i = 1; i < len; i += 2)
                compr[j++] = map(s[i-1]) << 4 | map(s[i]);

        if (i-1 < len)
                compr[j++] = map(s[i-1]) << 4;

        compr[j] = '[=10=]';
        return compr;
}

char *decompress(const char *s, int len)
{
        int i, j;
        char *decompr = malloc(2*len + 1);

        for (i = j = 0; i < len; i++) {
                decompr[j++] = revmap((s[i] & 0xf0) >> 4);
                decompr[j++] = revmap(s[i] & 0xf);
        }
        decompr[j] = '[=10=]';
        return decompr;
}

int main()
{
        const char *input = "230982 0 4294753011 -9 4294198951 -1 4294225518 0 0 0 524789 0 934585 0 4 0 0 0 0 ";
        int plen = strlen(input);
        printf("plain(len=%d): %s\n", plen, input);

        char *compr = compress(input, plen);
        int clen = strlen(compr);

        char *decompr = decompress(compr, clen);
        int dlen = strlen(decompr);
        printf("decompressed(len=%d): %s\n", dlen, decompr);

        free(compr);
        free(decompr);
}