zlib сжимает байтовый массив? - PullRequest
10 голосов
/ 08 июня 2011

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

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

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

Ответы [ 2 ]

29 голосов
/ 08 июня 2011

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

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

Логика дефляции в .NET демонстрирует поведенческую аномалию, при которой ранее сжатые данные могут быть значительно раздуты, значительно при «сжатии».Это было источником ошибки Connect, возникшей в 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,..@.@..5%.
0030    CE                                                  .

Для распаковки,

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

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


РЕДАКТИРОВАТЬ

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

Если вам не нужно сжатие "max", другими словами, если вы очень сильно хотите получить 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,..@.@..5%.
0030    CE                                                  .
0 голосов
/ 01 августа 2014

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

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

...