压缩txt文件中的数据

时间:2018-03-27 13:05:54

标签: text compression

我正在寻找建议我可以用来压缩以下数据的方法: 65|018 03C 066 066 066 07E 066 066 066 000 000 000 000 000 000 000 66|07C 066 066 066 07C 066 066 066 07C 000 000 000 000 000 000 000 67|03C 066 066 060 060 060 066 066 03C 000 000 000 000 000 000 000 ... , 格式化为 <ASCII_CODE>|<FIRST ROW OF PIXELS> ... <16TH ROW OF PIXELS>

其中一行pizel可以是3个十六进制数字。 并且要求阅读要求不高。我已经尝试过Run-Length编码(因为有很多冗余数据),但是效果不好。 它还需要足够简单才能解压缩。

1 个答案:

答案 0 :(得分:0)

最简单的方法是使用现有的解决方案,例如zlib,并在解析之前解压缩到内存。但这是一个无聊的答案,所以让我们看看我们自己能够取得的成就。

以文本形式表示数据效率很低。在当前格式中,我们需要68个字节(假设行终止符为CRLF)来表示一条记录。

记录本身由ASCII码(1个字节)和16行像素组成。每行由3位十六进制数表示,即12位。这总共25个字节,如果我们将2行打包成3个字节,或者如果我们只为每行使用16位整数则总共33个字节。这是原始空间要求的37%或49%,并且编码/解码它或多或少是微不足道的。

ASCII代码是连续的,但不是从0开始,并且不会一直运行到255.您可以按代码对记录进行排序,存储第一个和最后一个的索引,以及此范围的位图,指示缺少的符号。那么你就不必为每条记录存储代码。

似乎绝大多数符号左边是空列,底部是空行(顶部的空格不太常见)。

您可以存储此类空列和行的数量,然后避免保存这些位。由于有12列,4位就足够了。跳过12列或更多列意味着缺少字形(因此我们不需要前面提到的位图)。另外4位表示在底部跳过的行(因为跳过所有列就像跳过所有行一样,我们不需要能够跳过16行)。

您可以从Lempel-Ziv算法中获取一些灵感来压缩字形。

让我们定义以下操作码:

  • 0 - Verbatim Row。接下来的12位是像素。
  • 1 - 复制行。接下来的4位是要复制的行的偏移​​量(或可能是索引)。
  • 2 - [可选]空行。无需参数。当前行设置为全0。
  • 3 - [可选]结束字形。无需参数。当前和所有后续行都设置为0。

注意:空行可以表示为带0偏移的复制。

现在每个字形可以表示为那些操作码及其参数的序列。

字形图像中0和1的概率分布可能是偏斜的。类似的情况将与类似LZ的方案中的操作码和偏移相关。对它们进行熵编码可能是一个不错的选择(使用可以编码接近熵的算术编码器)。您还可以考虑使模型上下文敏感。

最后,您可以混合和匹配所提到的方法(如果LZ扩展数据,则回退到原始表示)。

包含许多变体的完整源代码在pastebin上,由于大小I,我将包含有趣的位。

样本输入的不同变体(0未经修改)的效率比较如下:

Variant 0: 6547 bytes
Variant 1: 3194 bytes
Variant 2: 2434 bytes
Variant 3: 1493 bytes
Variant 4: 1483 bytes
Variant 5: 1296 bytes
Variant 6: 1152 bytes
Variant 7: 1011 bytes
Variant 8: 839 bytes
Variant 9: 789 bytes
Variant 10: 669 bytes

表示内存中字体的简单结构:

struct glyph
{
    uint8_t code;
    std::array<uint16_t, 16> rows;
};

struct font
{
    std::string header;
    std::vector<glyph> glyphs;

    void sort()
    {
        std::sort(glyphs.begin(), glyphs.end()
            , [](glyph const& l, glyph const& r) -> bool {
                return l.code < r.code;
            });

    }
};

一个简单的输出比特流实现:

struct simple_bitstream
{
    simple_bitstream(std::vector<uint8_t>& buffer)
        : buf_(buffer)
        , temp_(0)
        , temp_size_(0)
    {
    }

    void write_bits(uint32_t v, uint8_t bits)
    {
        if (bits) {
            write_bits(v >> 1, bits - 1);
            write_bit(v & 1);
        }        
    }

    void write_bit(uint8_t v)
    {
        temp_ = (temp_ << 1) | (v & 1);
        ++temp_size_;
        if (temp_size_ == 8) {
            buf_.push_back(temp_);
            temp_size_ = 0;
            temp_ = 0;
        }
    }

    void flush()
    {
        for (; temp_size_;) {
            write_bit(0);
        }
    }

    std::vector<uint8_t>& buf_;
    uint8_t temp_;
    uint8_t temp_size_;
};

Variant 6 - Lempel-Ziv启发的算法,包含4个操作码。

// Find nearest identical preceding row in this glyph
uint8_t find_nearest_copy(glyph const& g, uint32_t i)
{
    uint8_t offset(0);
    uint16_t row(g.rows[i]);
    for (uint8_t j(1); j < i; ++j) {
        if (row == g.rows[i - j]) {
            offset = j;
            break;
        }
    }
    return offset;
}

uint32_t find_end_row(glyph const& g)
{
    uint32_t end_row(16);
    for (uint32_t i(0); i < 16; ++i) {
        if (g.rows[15 - i] > 0) {
            break;
        }
        --end_row;
    }
    return end_row;
}

void encode_v6(font const& f, std::vector<uint8_t>& buffer)
{
    uint32_t OP_VERBATIM(0), OP_COPY(1), OP_EMPTY(2), OP_END(3);

    encode_header(f, buffer);
    simple_bitstream b(buffer);
    for (glyph const& g : f.glyphs) {
        // Code using 1 byte
        b.write_bits(g.code, 8);

        uint32_t end_row(find_end_row(g));
        for (uint32_t i(0); i < end_row; ++i) {
            uint16_t row(g.rows[i]);
            if (row == 0) {
                // Empty row
                b.write_bits(OP_EMPTY, 2);
                continue;
            }

            // Find nearest identical preceding row in this glyph
            uint8_t offset(find_nearest_copy(g, i));
            if (offset) {
                // Copy with non-zero offset
                b.write_bits(OP_COPY, 2);
                b.write_bits(offset - 1, 4);
            } else {
                // Verbatim row
                b.write_bits(OP_VERBATIM, 2);
                b.write_bits(row, 12);
            }
        }
        if (end_row < 16) {
            // End the glyph (any remaining rows are empty)
            b.write_bits(OP_END, 2);
        }
    }
}

变体8-10使用FastAC(算术编解码器)进行熵编码,以及一些额外的简单建模。

相关问题