zlib сжимает массив байтов?

У меня есть этот несжатый массив байтов:

0E 7C BD 03 6E 65 67 6C 65 63 74 00 00 00 00 00 00 00 00 00 42 52 00 00 01 02 01
00 BB 14 8D 37 0A 00 00 01 00 00 00 00 05 E9 05 E9 00 00 00 00 00 00 00 00 00 00
00 00 00 00 01 00 00 00 00 00 81 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 05 00 00 01 00 00 00

И мне нужно сжать его, используя алгоритм deflate (реализованный в zlib), из того, что я искал, эквивалент в C # будет использовать GZipStream, но я вообще не могу сопоставить сжатый результат.

Вот код сжатия:

public byte[] compress(byte[] input)
{
    using (MemoryStream ms = new MemoryStream())
    {
        using (GZipStream deflateStream = new GZipStream(ms, CompressionMode.Compress))
        {
            deflateStream.Write(input, 0, input.Length);
        }
        return ms.ToArray();
    }
}

Вот результат приведенного выше кода сжатия:

1F 8B 08 00 00 00 00 00 04 00 ED BD 07 60 1C 49 96 25 26 2F 6D CA 7B 7F 4A F5 4A
D7 E0 74 A1 08 80 60 13 24 D8 90 40 10 EC C1 88 CD E6 92 EC 1D 69 47 23 29 AB 2A
81 CA 65 56 65 5D 66 16 40 CC ED 9D BC F7 DE 7B EF BD F7 DE 7B EF BD F7 BA 3B 9D
4E 27 F7 DF FF 3F 5C 66 64 01 6C F6 CE 4A DA C9 9E 21 80 AA C8 1F 3F 7E 7C 1F 3F
22 7E 93 9F F9 FB 7F ED 65 7E 51 E6 D3 F6 D7 30 CF 93 57 BF C6 AF F1 6B FE 5A BF
E6 AF F1 F7 FE 56 7F FC 03 F3 D9 AF FB 5F DB AF 83 E7 0F FE 35 23 1F FE BA F4 FE
AF F1 6B FC 1A FF 0F 26 EC 38 82 5C 00 00 00

Вот результат, который я ожидаю:

78 9C E3 AB D9 CB 9C 97 9A 9E 93 9A 5C C2 00 03 4E 41 0C 0C 8C 4C 8C 0C BB 45 7A
CD B9 80 4C 90 18 EB 4B D6 97 0C 28 00 2C CC D0 C8 C8 80 09 58 21 B2 00 65 6B 08
C8

Что я делаю не так, может ли кто-нибудь помочь мне там?


person Guapo    schedule 08.06.2011    source источник
comment
Почему вы ожидаете одинаковых результатов от разных реализаций? Существует множество способов сжатия содержимого, которое можно распаковать с помощью одного и того же декомпрессора. Но в вашем случае zip-поток, похоже, выводит какой-то заголовок.   -  person CodesInChaos    schedule 08.06.2011
comment
Мало того, что результат GZipStream отличается, он еще и больше, чем несжатый ввод!   -  person    schedule 08.06.2011
comment
@Inuyasha, что я уже понял, поэтому я ищу, как сделать их равными, пытаясь выяснить, что я делаю неправильно, как я уже упоминал, мне нужно использовать реализацию deflate zlib в C#. @CodeInChaos Я не знал, что это другая реализация, которую я искал в SO, и я нашел несколько ответов, в которых говорилось, что GZip был эквивалентом для него, я понял, что это не так, когда начал его тестировать.   -  person Guapo    schedule 08.06.2011
comment
Помимо увеличенного размера, я предполагаю, что есть еще одна программа, распаковывающая это. Как это происходит?   -  person Henk Holterman    schedule 08.06.2011


Ответы (2)


Во-первых, немного информации: DEFLATE — это алгоритм сжатия, он определен в RFC 1951. DEFLATE используется в форматах ZLIB и GZIP, определенных в RFC 1950 и 1952 соответственно, которые по сути являются тонкими оболочками вокруг потоков байтов DEFLATE. Оболочки предоставляют метаданные, такие как имя файла, временные метки, CRC или Adlers и т. д.

Библиотека базовых классов .NET реализует DeflateStream, который создает необработанный поток байтов DEFLATE при использовании для сжатия. При использовании в декомпрессии он потребляет необработанный поток байтов DEFLATE. .NET также предоставляет GZipStream, который представляет собой просто оболочку GZIP вокруг этой базы. В библиотеке базовых классов .NET нет ZlibStream — ничего, что производило бы или потребляло ZLIB. Есть несколько хитростей, как это сделать, можете поискать.

Логика дефляции в .NET демонстрирует поведенческую аномалию, когда ранее сжатые данные могут быть значительно увеличены при «сжатии». Это был источник ошибка подключения, возникшая в Microsoft, и обсуждалась здесь, на SO. Это может быть то, что вы видите, поскольку неэффективное сжатие. Microsoft отклонила ошибку, потому что, хотя она неэффективна для экономии места, сжатый поток не является недействительным, другими словами, он может быть «распакован» любым совместимым механизмом DEFLATE.

В любом случае, как сообщил кто-то другой, сжатый поток байтов, создаваемый разными компрессорами, не обязательно может быть одинаковым. Это зависит от их настроек по умолчанию и настроек компрессора, заданных приложением. Несмотря на то, что сжатые потоки байтов различаются, они все равно могут распаковываться в один и тот же исходный поток байтов. С другой стороны, то, что вы использовали для сжатия, было GZIP, в то время как кажется, что вы хотели, это ZLIB. Хотя они связаны, они не одинаковы; вы не можете использовать GZipStream для создания потока байтов ZLIB. Это основной источник разницы, которую вы видите.


Я думаю, вам нужен поток ZLIB.

Бесплатная управляемая Zlib в проекте DotNetZip реализует сжатие потоков для всех трех форматов (DEFLATE, ZLIB, GZIP). DeflateStream и GZipStream работают так же, как встроенные классы .NET, и там есть класс ZlibStream, который делает то, что вы думаете. Ни один из этих классов не демонстрирует описанной выше аномалии поведения.


В коде это выглядит так:

    byte[] original = new byte[] {
        0x0E, 0x7C, 0xBD, 0x03, 0x6E, 0x65, 0x67, 0x6C,
        0x65, 0x63, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x42, 0x52, 0x00, 0x00,
        0x01, 0x02, 0x01, 0x00, 0xBB, 0x14, 0x8D, 0x37,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x05, 0xE9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x81, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00,
        0x01, 0x00, 0x00, 0x00
    };

    var compressed = Ionic.Zlib.ZlibStream.CompressBuffer(original);

Вывод такой:

0000    78 DA E3 AB D9 CB 9C 97 9A 9E 93 9A 5C C2 00 03     x...........\...
0010    4E 41 0C 0C 8C 4C 8C 0C BB 45 7A CD 61 62 AC 2F     NA...L...Ez.ab./
0020    19 B0 82 46 46 2C 82 AC 40 FD 40 0A 00 35 25 07     ...FF,..@[email protected]%.
0030    CE                                                  .

Чтобы распаковать,

    var uncompressed = Ionic.Zlib.ZlibStream.UncompressBuffer(compressed);

Вы можете посмотреть документацию по статическому методу CompressBuffer< /а>.


ИЗМЕНИТЬ

Возникает вопрос, почему DotNetZip выдает 78 DA для первых двух байт вместо 78 9C? Разница несущественная. 78 DA кодирует "максимальное сжатие", а 78 9C кодирует "сжатие по умолчанию". Как видно из данных, для этой небольшой выборки фактическое количество сжатых байтов точно такое же, как при использовании BEST, так и при использовании DEFAULT. Кроме того, информация об уровне сжатия не используется во время распаковки. Это не имеет никакого эффекта в вашем приложении.

Если вы не хотите "максимального" сжатия, другими словами, если вы очень настроены на получение 78 9C в качестве первых двух байтов, даже если это не имеет значения, вы не можете использовать вспомогательную функцию CompressBuffer, которая использует лучшее сжатие. уровень под одеялом. Вместо этого вы можете сделать это:

  var compress = new Func<byte[], byte[]>( a => {
        using (var ms = new System.IO.MemoryStream())
        {
            using (var compressor =
                   new Ionic.Zlib.ZlibStream( ms, 
                                              CompressionMode.Compress,
                                              CompressionLevel.Default )) 
            {
                compressor.Write(a,0,a.Length);
            }

            return ms.ToArray();
        }
    });

  var original = new byte[] { .... };
  var compressed = compress(original);

Результат:

0000    78 9C E3 AB D9 CB 9C 97 9A 9E 93 9A 5C C2 00 03     x...........\...
0010    4E 41 0C 0C 8C 4C 8C 0C BB 45 7A CD 61 62 AC 2F     NA...L...Ez.ab./
0020    19 B0 82 46 46 2C 82 AC 40 FD 40 0A 00 35 25 07     ...FF,..@[email protected]%.
0030    CE                                                  .
person Cheeso    schedule 08.06.2011
comment
@Cheeso Я только что попробовал ZLib.Net из ответа Мерлина, и он отлично работает, чтобы сжать данные, которые я ожидал, теперь я просто не знаю, как распаковать массив байтов, который я получил. - person Guapo; 08.06.2011
comment
@Cheeso, спасибо. Распаковать с его помощью довольно просто. Я попробую, так как у меня возникли проблемы с распаковкой из другой библиотеки. - person Guapo; 08.06.2011
comment
@Cheeso DotNetZip всегда сжимает его с 1 другим байтом 78 DA вместо 78 9C в самом начале, в то время как когда я использую ZLib.Net, он отлично работает, давая мне 9C вместо DA, удаляя, что он отлично работает, чтобы распаковать, не уверен, почему он меняется 9C до DA еще... - person Guapo; 09.06.2011
comment
На самом деле не имеет значения, является ли второй байт 9C или DA. ZLIB имеет 2-байтовый заголовок, первый байт указывает метод сжатия и размер окна, если используется DEFLATE. Это всегда 78. Следующий байт меняется и указывает на 3 вещи: использовался ли предустановленный словарь, уровень сжатия и своего рода контрольная сумма в первых двух байтах. По сути, 9C указывает уровень сжатия по умолчанию, а DA указывает максимальный уровень сжатия. Эта информация не нужна для декомпрессии; это интересно только в том случае, если ваше приложение считает, что дополнительное сжатие может быть полезным. Вы можете игнорировать это. - person Cheeso; 09.06.2011
comment
Удобный метод CompressBuffer указывает наилучшее сжатие, поэтому он кодируется как 78 DA в выходном буфере, который вы видите. - person Cheeso; 09.06.2011
comment
Немного дополнительной информации: DEFLATE также используется в формате файла zip (для каждого файла в архиве отдельно). - person Paŭlo Ebermann; 30.06.2011
comment
@Cheeso, я знаю, что это старая тема, но ваш подробный ответ побудил меня задать вам пару вопросов. Когда я попытался использовать zlib в C# для сжатия в памяти (не в файле) с малыми байтами [] около 500 байт, я обнаружил, что соотношения довольно противоречивы: 125 => 116, 98 => 90, 115 => 113 (байт до и после сжатия). Это подводит меня к вопросу: ожидается ли такое несоответствие соотношения для всех инструментов сжатия; или это имеет какое-либо отношение к небольшим входным массивам байтов и улучшается ли оно с большими входными данными? Был бы очень рад узнать ваши мысли по этому поводу. Большое спасибо. - person Pradeep Puranik; 14.12.2017

Проще говоря, то, что вы получили, имело заголовок GZip. Вам нужен более простой заголовок Zlib. ZLib имеет опции для заголовка GZip, заголовка Zlib или без заголовка. Обычно заголовок Zlib используется, если данные не связаны с файлом на диске (в этом случае используется заголовок GZip). По-видимому, в библиотеке .Net нет возможности написать заголовок zlib (хотя это, безусловно, самый распространенный способ). заголовок, используемый в форматах файлов). Попробуйте http://dotnetzip.codeplex.com/.

Вы можете быстро протестировать все различные параметры zlib, используя HexEdit (Операции->Сжатие->Настройки). См. http://www.hexedit.com . Мне потребовалось 10 минут, чтобы проверить ваши данные, просто вставив ваши сжатые байты в HexEdit и распаковав. Также попытался сжать исходные байты с помощью заголовков GZip и ZLib в качестве перепроверки. Обратите внимание, что вам, возможно, придется повозиться с настройками, чтобы получить именно те байты, которые вы ожидали.

person Andrew W. Phillips    schedule 01.08.2014