Что если данные, сжатые с помощью GZipStream или DeflateStream, длиннее необработанных данных? - PullRequest
2 голосов
/ 26 января 2011

Я не специалист по форматам, но, полагаю, некоторые входные данные для сжатых данных могут быть длиннее из-за накладных расходов на форматирование.

Я согласен с этим, ночто меня не устраивает, так это задокументированное поведение параметра count для GZipStream / DeflateStream.Write (): «Максимальное количество сжатых байтов для записи».Обычная практика (если не выполнять сжатие кусками) - передавать длину входных данных:

public static byte[] Compress(byte[] data)
{
    using (var compressed = new IO.MemoryStream(data.Length))
    {
        using (var compressor = new IO.Compression.DeflateStream(compressed, IO.Compression.CompressionMode.Compress))
            compressor.Write(data, 0, data.Length);
        return compressed.ToArray();
    }
}

В крайнем случае, о котором я говорю, оператор write не будет записывать весь сжатыйпоток данных, только первые данные. Длина его байтов.Я мог бы просто удвоить размер буфера, но для больших наборов данных это немного расточительно, и в любом случае мне не нравятся догадки.

Есть ли лучший способ сделать это?

Ответы [ 5 ]

4 голосов
/ 26 января 2011

Я почти уверен, что это ошибка в документации.Документация в более ранних версиях гласит «Количество сжатых байтов», что соответствует принципу работы всех других потоков.

То же изменение было внесено в документацию метода Read, где это имеет смысл,но я думаю, что изменение было внесено по ошибке в документацию метода Write.Кто-то исправил документацию по методу Read и подумал, что такое же исправление применимо и к методу Write.

Нормальное поведение для метода Read потока заключается в том, что он может возвращатьменьше данных, чем запрошено, и метод возвращает количество байтов, фактически помещенных в буфер.С другой стороны, метод Write всегда записывает все указанные данные.Для метода не имеет смысла записывать меньше данных в любой реализации.Так как метод не имеет возвращаемого значения, он не может вернуть количество записанных байтов.

Указанное число не является размером вывода, это размер данных, которые вы отправляете в метод,Если выходные данные больше входных данных, они все равно будут записаны в поток.

Редактировать:

Я добавил комментарий об этом к содержанию сообщества документации по методу вБиблиотека MSDN.Посмотрим, продолжит ли это Microsoft ...

2 голосов
/ 26 января 2011

В этом случае документация плохо сформулирована. The maximum number of compressed bytes to write в этом случае означает количество байтов из источника, который вы хотите записать как сжатые данные. Вы можете проверить это, пытаясь сжать одну букву, которая закодирована с использованием кодировки ASCII. Длина буфера, очевидно, будет равна 1, но из него вы получите массив из 108 байт.

2 голосов
/ 26 января 2011

Вы правы.Если алгоритм сжатия делает некоторые входные данные короче, то некоторые другие должны становиться длиннее.Это следует из принципа pigeonhole .

. Многие алгоритмы работают в худшем случае, так как если данные расширяются слишком сильно, они могут вместо этого выбрать вставку несжатого блока в поток, который простозаголовок в несколько байтов, а затем копия исходных данных в несжатом виде.

Например, алгоритм DEFLATE имеет такую ​​функцию:

3.2.4. Non-compressed blocks (BTYPE=00)

         Any bits of input up to the next byte boundary are ignored.
         The rest of the block consists of the following information:

              0   1   2   3   4...
            +---+---+---+---+================================+
            |  LEN  | NLEN  |... LEN bytes of literal data...|
            +---+---+---+---+================================+

         LEN is the number of data bytes in the block.  NLEN is the
         one's complement of LEN.

Так что, если вы добавитеместа для заголовков плюс дополнительный 1%, с вами все будет в порядке.

Если вы хотите проверить, работает ли ваш код, когда сжатый вывод больше, чем ввод, тогда вы можете попробовать сгенерировать несколько килобайт совершенно случайных данных.и попробуйте сжать это.Весьма вероятно, что выходной сигнал будет длиннее входного, если вы выберете байты равномерно случайным образом.

1 голос
/ 26 января 2011

По словам Жана-Лупа Гэйли и сопровождающих zlib (zlib - это алгоритм сжатия, лежащий в основе gzip, и zip, полученный из оригинального приложения PKWare Zip), «_сжимающий в настоящее время метод сжатия в zlib практически никогда не расширяет данные.

В отличие от LZW, используемого в * nix компрессах (1) и GIF-изображениях, которые могут удвоить или утроить размер ввода.Попробуйте запустить сжатие для сжатого или зашифрованного файла и посмотрите, что вы получите.Затем попробуйте запустить gzip для сжатого файла и посмотрите, что произойдет.

http://www.zlib.net/

Как уже отмечалось, для вырожденного ввода размер gzipped просто потребует небольшого объема служебной информации для требуемого заголовка иблоки управления.

0 голосов
/ 26 января 2011

Спасибо за великолепные и очень быстрые ответы.Вы, ребята, потрясающие.

После небольшого количества копаний кажется, что в .NET 4 (разве я не говорил, что я использовал .NET 4 :)) добавлен новый метод CopyTo, который делает всенамного проще.

public static byte[] Compress(byte[] data)
{
    using (var rawData = new IO.MemoryStream(data))
    using (var compressed = new IO.MemoryStream(data.Length))
    {
        using (var compressor = new IO.Compression.DeflateStream(compressed, IO.Compression.CompressionMode.Compress))
            rawData.CopyTo(compressor);
        return compressed.ToArray();
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...