发送以 Python 压缩的 zip 存档但尝试在 Rust 中解压缩时出现问题
problem sending zip archive compressed in Python but trying to decompress in Rust
我有这段代码可以在 Python
中用 zlib
(gzip
) 压缩数据
dta = bytes(str("..."))
res = zlib.compress(dta)
with open('packed.gz', 'wb') as f:
f.write(t)
我想用 Rust 打开它
use std::io::prelude::*;
use flate2::read::GzDecoder; // flate2 = "1.0"
use std::fs::File;
fn main() {
let f = File::open("packed.gz").unwrap();
let mut d = GzDecoder::new(f);
let mut s = String::new();
d.read_to_string(&mut s).unwrap();
println!("{}", s);
}
然后我得到
thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: Custom { kind: InvalidInput, error: "invalid gzip header" }', src/main.rs:11:30
文件格式不同有问题吗?
您的 python 程序生成的数据流实际上不是 .gz
文件,而是原始 DEFLATE-stream。如果你想要一个 .gz
文件(包括 gz-header,这是 flate2::read::GzDecoder
期望的),请使用 Python 中的 gzip
模块。如果您确实想要原始 DEFLATE-stream(由 zlib.compress
生成),请使用 flate2::read::DeflateDecoder
解压缩数据。
确实 zlib
压缩与 python stdlib documentation 提到的 gzip 兼容。那是因为 zlib 和 gzip 都使用 deflate
压缩标准。
zlib
生成原始的 deflate 压缩字节流。另一方面,gzip
是单一压缩的标准,它将压缩流包装在 deflate 上。所以如果你从 gzip 文件中删除文件 headers,你可以使用原始 deflate 库解压缩。
字符串 This is the raw string
的 zlib
压缩流看起来像
>>> import zlib
>>> data = bytes(str("This is the raw string"), encoding="utf-8")
>>> res = zlib.compress(data)
>>> print(res)
b'x\x9c\x0b\xc9\xc8,V\x00\xa2\x92\x8cT\x85\xa2\xc4r\x85\xe2\x92\xa2\xcc\xbct\x00ZI\x08\x17'
这是(当你把它发送到 xxd
.
00000000: 789c 0bc9 c82c 5600 a292 8c54 85a2 c472 x....,V....T...r
00000010: 85e2 92a2 ccbc 7400 5a49 0817 ......t.ZI..
前 2 个字节声明文件的类型。
78 01 - No Compression/low
78 9C - Default Compression
78 DA - Best Compression
在我们的例子中,它是 78 9C
,它是具有默认压缩的 zlib,这是 python 在其文档中所说的。您可以通过 运行
来验证
file package.gz
它应该说 zlib compressed data
因为这就是文件 header 的样子。
如果我们尝试使用 gzip 压缩,让我们看看会发生什么。
>>> import gzip
>>> data = bytes(str("This is the raw string"), encoding="utf-8")
>>> gzip.compress(data)
b'\x1f\x8b\x08\x00\xd6\x1bw_\x02\xff\x0b\xc9\xc8,V\x00\xa2\x92\x8cT\x85\xa2\xc4r\x85\xe2\x92\xa2\xcc\xbct\x00&\x91\x1a\x82\x16\x00\x00\x00'
如果您注意到十六进制转储
00000000: 1f8b 0800 d61b 775f 02ff 0bc9 c82c 5600 ......w_.....,V.
00000010: a292 8c54 85a2 c472 85e2 92a2 ccbc 7400 ...T...r......t.
00000020: 2691 1a82 1600 0000 &.......
幻数显示,1f 8b
(wiki)。如果你查看 headers,你会看到两个十六进制转储包含相同的压缩数据,即
对于 zlib
00000000: .... 0bc9 c82c 5600 a292 8c54 85a2 c472 x....,V....T...r
00000010: 85e2 92a2 ccbc 7400 .... .... ......t.ZI..
并且,对于 gzip
00000000: .... .... .... .... .... 0bc9 c82c 5600 ......w_.....,V.
00000010: a292 8c54 85a2 c472 85e2 92a2 ccbc 7400 ...T...r......t.
00000020: .... .... .... .... &.......
所以如果你ask rust to use the correct decoder,它肯定会起作用。这就是标准的好处。
因此,gzip 只是 zlib 容器之上的一个容器。我还没有解释 hexdump 中其他字节的含义(因为我还没有查找)。
原来flate2
里有ZlibDecoder
...而且有效
我有这段代码可以在 Python
中用zlib
(gzip
) 压缩数据
dta = bytes(str("..."))
res = zlib.compress(dta)
with open('packed.gz', 'wb') as f:
f.write(t)
我想用 Rust 打开它
use std::io::prelude::*;
use flate2::read::GzDecoder; // flate2 = "1.0"
use std::fs::File;
fn main() {
let f = File::open("packed.gz").unwrap();
let mut d = GzDecoder::new(f);
let mut s = String::new();
d.read_to_string(&mut s).unwrap();
println!("{}", s);
}
然后我得到
thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: Custom { kind: InvalidInput, error: "invalid gzip header" }', src/main.rs:11:30
文件格式不同有问题吗?
您的 python 程序生成的数据流实际上不是 .gz
文件,而是原始 DEFLATE-stream。如果你想要一个 .gz
文件(包括 gz-header,这是 flate2::read::GzDecoder
期望的),请使用 Python 中的 gzip
模块。如果您确实想要原始 DEFLATE-stream(由 zlib.compress
生成),请使用 flate2::read::DeflateDecoder
解压缩数据。
确实 zlib
压缩与 python stdlib documentation 提到的 gzip 兼容。那是因为 zlib 和 gzip 都使用 deflate
压缩标准。
zlib
生成原始的 deflate 压缩字节流。另一方面,gzip
是单一压缩的标准,它将压缩流包装在 deflate 上。所以如果你从 gzip 文件中删除文件 headers,你可以使用原始 deflate 库解压缩。
字符串 This is the raw string
的 zlib
压缩流看起来像
>>> import zlib
>>> data = bytes(str("This is the raw string"), encoding="utf-8")
>>> res = zlib.compress(data)
>>> print(res)
b'x\x9c\x0b\xc9\xc8,V\x00\xa2\x92\x8cT\x85\xa2\xc4r\x85\xe2\x92\xa2\xcc\xbct\x00ZI\x08\x17'
这是(当你把它发送到 xxd
.
00000000: 789c 0bc9 c82c 5600 a292 8c54 85a2 c472 x....,V....T...r
00000010: 85e2 92a2 ccbc 7400 5a49 0817 ......t.ZI..
前 2 个字节声明文件的类型。
78 01 - No Compression/low
78 9C - Default Compression
78 DA - Best Compression
在我们的例子中,它是 78 9C
,它是具有默认压缩的 zlib,这是 python 在其文档中所说的。您可以通过 运行
file package.gz
它应该说 zlib compressed data
因为这就是文件 header 的样子。
如果我们尝试使用 gzip 压缩,让我们看看会发生什么。
>>> import gzip
>>> data = bytes(str("This is the raw string"), encoding="utf-8")
>>> gzip.compress(data)
b'\x1f\x8b\x08\x00\xd6\x1bw_\x02\xff\x0b\xc9\xc8,V\x00\xa2\x92\x8cT\x85\xa2\xc4r\x85\xe2\x92\xa2\xcc\xbct\x00&\x91\x1a\x82\x16\x00\x00\x00'
如果您注意到十六进制转储
00000000: 1f8b 0800 d61b 775f 02ff 0bc9 c82c 5600 ......w_.....,V.
00000010: a292 8c54 85a2 c472 85e2 92a2 ccbc 7400 ...T...r......t.
00000020: 2691 1a82 1600 0000 &.......
幻数显示,1f 8b
(wiki)。如果你查看 headers,你会看到两个十六进制转储包含相同的压缩数据,即
对于 zlib
00000000: .... 0bc9 c82c 5600 a292 8c54 85a2 c472 x....,V....T...r
00000010: 85e2 92a2 ccbc 7400 .... .... ......t.ZI..
并且,对于 gzip
00000000: .... .... .... .... .... 0bc9 c82c 5600 ......w_.....,V.
00000010: a292 8c54 85a2 c472 85e2 92a2 ccbc 7400 ...T...r......t.
00000020: .... .... .... .... &.......
所以如果你ask rust to use the correct decoder,它肯定会起作用。这就是标准的好处。
因此,gzip 只是 zlib 容器之上的一个容器。我还没有解释 hexdump 中其他字节的含义(因为我还没有查找)。
原来flate2
里有ZlibDecoder
...而且有效