我不是格式方面的专家,但我猜测由于格式化开销,压缩数据的某些输入数据实际上可能会更长。
我没关系,但我不熟悉的是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字节。我可以将缓冲区大小加倍,但对于大数据集来说有点浪费,无论如何我不喜欢猜测。
有更好的方法吗?
答案 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,看看会发生什么。
如上所述,对于简并输入,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();
}
}