#python #rust #gzip
#python #Ржавчина #gzip
Вопрос:
У меня есть этот код, который сжимает данные с zlib
помощью ( gzip
) в Python
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(amp;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
есть ли проблема с различиями в формате файлов?
Ответ №1:
Поток данных, сгенерированный вашей программой на python, на самом деле не является .gz
файлом, это необработанный поток DEFLATE. Если вам нужен a .gz
-file (включая ожидаемый gz-заголовок flate2::read::GzDecoder
), используйте gzip
модуль на Python. Если вам нужен необработанный поток DEFLATE (созданный zlib.compress
), используйте flate2::read::DeflateDecoder
для распаковки данных.
Ответ №2:
Это правда, что zlib
сжатие совместимо с gzip, как указано в документации python stdlib. Это связано с тем, что zlib и gzip используют deflate
стандарт сжатия.
zlib
создает необработанный поток сжатых байтов deflate. С другой стороны gzip
, это стандарт для однократного сжатия, который оборачивает сжатый поток поверх deflate. Итак, если вы удалите заголовки файлов из файла gzip, вы можете распаковать его с помощью библиотеки raw deflate.
zlib
Сжатый поток для строки This is the raw string
выглядит так
>>> import zlib
>>> data = bytes(str("This is the raw string"), encoding="utf-8")
>>> res = zlib.compress(data)
>>> print(res)
b'xx9cx0bxc9xc8,Vx00xa2x92x8cTx85xa2xc4rx85xe2x92xa2xccxbctx00ZIx08x17'
Который (когда вы отправляете его на 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
, как выглядит заголовок файла.
Если мы попытаемся сжать с помощью gzip, давайте посмотрим, что получится.
>>> import gzip
>>> data = bytes(str("This is the raw string"), encoding="utf-8")
>>> gzip.compress(data)
b'x1fx8bx08x00xd6x1bw_x02xffx0bxc9xc8,Vx00xa2x92x8cTx85xa2xc4rx85xe2x92xa2xccxbctx00amp;x91x1ax82x16x00x00x00'
Если вы заметили шестнадцатеричный дамп
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 amp;.......
Магическое число показывает, 1f 8b
(wiki). Если вы посмотрите мимо заголовков, вы увидите, что оба шестнадцатеричных дампов содержат одинаковые сжатые данные, которые
Для 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: .... .... .... .... amp;.......
Поэтому, если вы попросите rust использовать правильный декодер, он обязательно сработает. В этом преимущество стандартов.
Итак, gzip — это просто контейнер поверх контейнера zlib. Я не объяснил, что означают другие байты в шестнадцатеричном дампе (потому что я еще не посмотрел его).
Ответ №3:
оказывается, что есть ZlibDecoder
flate2
… и это работает