如果使用GZipStream或DeflateStream压缩的数据比原始数据长?

时间:2011-01-25 22:33:45

标签: .net compression

我不是格式方面的专家,但我猜测由于格式化开销,压缩数据的某些输入数据实际上可能会更长。

我没关系,但我不熟悉的是gnipStream / DeflateStream.Write()中count参数的记录行为:“要写入的最大压缩字节数。”通常的做法(除非在块中压缩)是传递输入数据的长度:

public static byte[] Compress(byte[] data)
{
    using (var compressed = new IO.MemoryStream(data.Length))
    {
        using (var compressor = new IO.Compression.DeflateStream(compressed, IO.Compression.CompressionMode.Compress))
            compressor.Write(data, 0, data.Length);
        return compressed.ToArray();
    }
}

在我所说的边缘情况下,write语句不会写出整个压缩数据流,只会写出第一个data.Length字节。我可以将缓冲区大小加倍,但对于大数据集来说有点浪费,无论如何我不喜欢猜测。

有更好的方法吗?

5 个答案:

答案 0 :(得分:4)

我很确定这是文档中的错误。早期版本中的文档读取“压缩的字节数”,这与所有其他流的工作方式一致。

Read方法的文档进行了相同的更改,它有意义,但我认为更改是错误地对Write方法的文档进行的。有人更正了Read方法的文档,并认为同样的更正也适用于Write方法。

流的Read方法的正常行为是它可以返回比请求的数据少的数据,并且该方法返回实际放置在缓冲区中的字节数。另一方面,Write方法总是写入指定的所有数据。在任何实现中编写较少数据的方法都没有任何意义。由于该方法没有返回值,因此无法返回写入的字节数。

指定的计数不是输出的大小,而是您发送到方法的数据的大小。如果输出大于输入,它仍将全部写入流。

编辑:

我在MSDN Library中的方法文档的社区内容中添加了对此的评论。让我们看看微软是否跟进......

答案 1 :(得分:2)

你是对的。如果压缩算法使某些输入更短,那么其他一些输入必须变得更长。这来自pigeonhole principle

许多算法具有良好的最坏情况行为,因为如果数据扩展太多,他们可以选择将非压缩块插入到流中,这只是几个字节的头,然后是未压缩形式的原始数据的副本。

例如DEFLATE算法具有此功能:

3.2.4. Non-compressed blocks (BTYPE=00)

         Any bits of input up to the next byte boundary are ignored.
         The rest of the block consists of the following information:

              0   1   2   3   4...
            +---+---+---+---+================================+
            |  LEN  | NLEN  |... LEN bytes of literal data...|
            +---+---+---+---+================================+

         LEN is the number of data bytes in the block.  NLEN is the
         one's complement of LEN.

因此,如果您为标题添加空间加上额外的1%,那么您应该没问题。

如果要在压缩输出大于输入时测试代码是否有效,那么可以尝试生成几千字节的完全随机数据并尝试压缩它。如果您选择均匀随机的字节,输出的输出很可能比输入长。

答案 2 :(得分:2)

在这种情况下,文档措辞不佳。在这种情况下,The maximum number of compressed bytes to write表示要作为压缩数据写入的源的字节数。您可以通过尝试压缩使用ASCII编码编码的单个字母来测试此问题。缓冲区长度明显为1,但是你将获得一个108字节的数组。

答案 3 :(得分:1)

根据Jean-Loup Gailly和zlib维护者(zlib是基于gzip的压缩算法,以及源自原始PKWare Zip应用程序的zip),“_目前在zlib中使用的压缩方法基本上从不扩展数据。”

与* nix的compress(1)和GIF图像中使用的LZW不同,GIF图像可以是输入大小的两倍或三倍。尝试对压缩或加密的文件运行压缩,看看你得到了什么。然后尝试对压缩文件运行gzip,看看会发生什么。

http://www.zlib.net/

如上所述,对于简并输入,gzip压缩的大小只会对所需的头和控制块产生一点点开销。

答案 4 :(得分:0)

感谢非常快速的答案。你们真棒。

经过一番挖掘,似乎.NET 4(我不是告诉你我使用的是.NET 4 :))添加了一个新的CopyTo方法,这使得整个事情变得更加容易。

public static byte[] Compress(byte[] data)
{
    using (var rawData = new IO.MemoryStream(data))
    using (var compressed = new IO.MemoryStream(data.Length))
    {
        using (var compressor = new IO.Compression.DeflateStream(compressed, IO.Compression.CompressionMode.Compress))
            rawData.CopyTo(compressor);
        return compressed.ToArray();
    }
}