以下代码(在 Node js v0.10.28 上):
var zlib = require('zlib');
var buf = new Buffer('uncompressed');
zlib.gzip(buf, function (err, result) {
console.log(result.toString('base64'));
});
产生字符串:
在 Win 7 x64 上:
H4sIAAAAAAAACyvNS87PLShKLS5OTQEA3a5CsQwAAAA=
^ ^
on Mac
H4sIAAAAAAAAAyvNS87PLShKLS5OTQEA3a5CsQwAAAA
^ ^
在 CentOs 上(Linux 2.6.32-279.19.1.el6.x86_64)
H4sIAAAAAAAAAyvNS87PLShKLS5OTQEA3a5CsQwAAAA=
^ ^
看来结局不同=
和第 13 个字符 (C
vs A
)但我不知道为什么。
一般差异的一些理论来源
gzip
(RFC 1952 https://www.ietf.org/rfc/rfc1952.txt),它使用deflate
(RFC 1951 https://www.ietf.org/rfc/rfc1951.txt)作为其压缩格式,从技术上讲只是一种文件格式规范。特别是,算法在如何选择压缩给定的字节方面被赋予了很大的自由度(这实际上是一种优势)。
有两种基本的压缩机制可以与deflate
:
长度有限的哈夫曼编码:出现频率较高的字符可以指定较短的位序列,出现频率较低的字符可以指定较长的位序列,从而导致表示相同信息的总体位数较少。用于编码的霍夫曼树可以根据输入(或其一部分)动态计算,也可以是固定的。因此,不同的编码器可能对相同的输入使用不同的霍夫曼树,从而导致树本身和编码字符的不同表示。
LZ77压缩:之前输出过的子串已经不需要再次输出;相反,只需要输出具有相同子字符串长度的反向引用。由于在给定输入中查找所有常见子字符串是一个 Hard Problem™,因此使用给定启发式查找尽可能多的子字符串通常会更有效(例如,跟踪以每个两个字符前缀开头的最后 6 个子字符串)。同样,不同的编码器可以(有效地)为相同的输入产生不同的输出。
最后,所有这些压缩数据被分散到一个或多个blocks,并且由编码器决定何时切换到新块。理论上,这甚至可以对每个字节进行(尽管这并不是真正的压缩!)。当结束一个块时,因为它的内容是使用霍夫曼位代码编码的,所以该块可能不会在字节边界上结束;在这种情况下,如果流中的后续项必须从整个字节开始(例如,未压缩的块必须从字节边界开始),则可以添加任意位作为填充以舍入到下一个字节。
正如您所看到的,相同输入的压缩字节可能有多种不同!即使使用相同的算法(例如规范的zlib库 http://www.zlib.net/,不要与同名的 RFC (1950) 混淆),不同的压缩级别通常会导致不同的结果。甚至可以想象,相同的程序在相同的环境中使用相同的输入和选项运行多次会产生不同的结果,例如由于数据结构对指针进行排序或使用指针作为散列值——指针值在执行之间可能会发生变化。此外,多线程实现本质上往往是不确定的。简而言之,您不应依赖给定输入的输出相同,除非您使用的实现明确提供了这种保证。 (尽管大多数理智的实现都力求确定性,但这在技术上并不是必需的。)
为什么您的具体示例 Base64 字符串有所不同
抛开尾随的差异=
等一下,你的三个例子中有两个具有完全相同的表示。这两者仅相差一位(C
-> A
)在第十个字节的第一部分(Base64 将字节三元组编码为 Base-64 字符的四元组,因此第 13 个 Base64 字符是第十个字节的前六位)。A
代表 0,并且C
代表2
-- 但请记住,这是字节的高六位,因此它实际上是 0 和 8 加上低两位。这些低两位是下一个 Base64 字符的高两位,y
: y
代表50,即110010
以二进制表示,因此第十个字节的低两位是0b11
,或 3。将它们放在一起,第十个字节是不同的,其值在一种实现中为 11,在另一种实现中为 3。快速浏览一下gzip
RFC揭示了第十个字节指示执行编码的操作系统/文件系统:果然,11被定义为“NTFS文件系统(NT)”,3被定义为“Unix”。因此,这种情况下的差异完全是由于您执行编码的操作系统造成的。 (请注意,任何第二个双字gzip
file 是时间戳,在您的示例中设置为 0(不可用),但在所有三个试验中很容易有很大差异,从而使差异更难以发现。)
至于尾随的=
,这只是 Base64 的填充(如维基百科上解释得很好 http://en.wikipedia.org/wiki/Base64)。由于 Base64 采用三个字节为一组并使用四个字节对其进行编码,因此如果编码的字节数不能被三整除,则使用最小数量的 Base64 数字(将输入末尾之后的字节视为空字节): byte,只需要两个Base64数字;对于两个人来说,只需要三个。这=
添加符号只是将 Base64 数字的数量四舍五入到四的倍数;你会注意到这意味着=
解码 Base64 字符串实际上并不需要符号,因为您知道它的长度(但如果字符串长度不是 4 的倍数,一些 Base64 解码器将拒绝该字符串)。因此,您的第二个和第三个示例表示完全相同的字节值,但由不同的 Base64 编码器生成。
你有它!我认为我的回答太冗长了,几乎可以归结为一个只有一句话答案的棘手问题,但我忍不住详细解释一切:-)
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)