проблема с отправкой zip-архива, сжатого в Python, но пытающегося распаковать в Rust

#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 … и это работает