Golang/Python的zlib区别

7
调试Python的zlib和golang的zlib之间的差异。为什么以下代码没有相同的结果? compress.go:
package main

import (
    "compress/flate"
    "bytes"
    "fmt"
)


func compress(source string) []byte {
    w, _ := flate.NewWriter(nil, 7)
    buf := new(bytes.Buffer)

    w.Reset(buf)
    w.Write([]byte(source))
    w.Close()

    return buf.Bytes()
}


func main() {
    example := "foo"
    compressed := compress(example)
    fmt.Println(compressed)
}

compress.py:

from __future__ import print_function

import zlib


def compress(source):
    # golang zlib strips header + checksum
    compressor = zlib.compressobj(7, zlib.DEFLATED, -15)
    compressor.compress(source)
    # python zlib defaults to Z_FLUSH, but 
    # https://golang.org/pkg/compress/flate/#Writer.Flush
    # says "Flush is equivalent to Z_SYNC_FLUSH"
    return compressor.flush(zlib.Z_SYNC_FLUSH)


def main():
    example = u"foo"
    compressed = compress(example)
    print(list(bytearray(compressed)))


if __name__ == "__main__":
    main()

结果

$ go version
go version go1.7.3 darwin/amd64
$ go build compress.go
$ ./compress
[74 203 207 7 4 0 0 255 255]
$ python --version
$ python 2.7.12
$ python compress.py
[74, 203, 207, 7, 0, 0, 0, 255, 255]

Python版本的第五个字节为0,但golang版本为4 -- 是什么导致了不同的输出?

zlib使用DEFLATE实现,但flate和zlib并不相同。在这里,你正在关闭Go中的flate流,而在Python中只是刷新。如果您将Go代码更改为Flush(),则输出将相同。还要注意,不同的实现不能保证产生相同的二进制输出,它们只能保证产生兼容的输出。 - JimB
@jimb:https://golang.org/pkg/compress/flate/ 上写着“Package flate implements the DEFLATE compressed data format, described in RFC 1951...”,这似乎与第一句话相矛盾,或者我理解有误(或文档有误=))。同样地,https://golang.org/pkg/compress/flate/#Writer.Close 声称刷新写入器并在 w.Write(...) 行后添加显式的 w.Flush() 会增加更多的填充/校验和字节。您能否提供更多细节,因为显然有些事情我没有理解? - everial
1
这个输出与Python输出相匹配:https://play.golang.org/p/_SCAspI3Mq。我不明白你发现了什么矛盾之处;你在Python中使用的是zlib,它内部使用DEFLATE来生成zlib格式的输出,而你的Go示例直接使用DEFLATE实现。我不知道你是否可以让Python的zlib库输出原始的、完整的DEFLATE流,但试图让不同的库输出压缩数据的逐字节匹配似乎并不有用或可维护。 - JimB
这个输出与Python输出匹配:play.golang.org/p/_SCAspI3Mq 啊,感谢澄清(我误读了“更改”为“添加”)。 我不知道你是否可以让Python的zlib库输出原始的、完整的DEFLATE流,但是试图让不同的库输出压缩数据的逐字节匹配似乎并没有什么用处或可维护性。 - everial
我在往返中遇到了问题,并试图通过隔离压缩位来制作一个更简单的示例。感谢您的建议和帮助 - 如果您愿意将您的评论转换为答案,我很高兴接受它。 - everial
1个回答

4

Python示例的输出不是“完整”的流,仅在压缩第一个字符串后刷新缓冲区。您可以通过将Close()替换为Flush()来从Go代码中获得相同的输出:

https://play.golang.org/p/BMcjTln-ej

func compress(source string) []byte {
    buf := new(bytes.Buffer)
    w, _ := flate.NewWriter(buf, 7)
    w.Write([]byte(source))
    w.Flush()

    return buf.Bytes()
}

然而,您正在比较Python中使用DEFLATE来生成zlib格式输出的zlib的输出和Go中实现的DEFLATE的flate,我不知道是否可以使Python zlib库输出原始完整的DEFLATE流,但是试图让不同的库输出逐字节匹配的压缩数据似乎没有用处或可维护性。压缩库的输出仅保证兼容,而非相同。

抱歉让你等这么久才接受答案,我错过了通知。感谢你写得这么好。=) - everial

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接