сжатие и распаковка исходных данных дает результат, отличный от исходных данных - PullRequest
2 голосов
/ 03 октября 2010

В моем приложении мне нужно распаковать данные, записанные DataContractSerializer для сжатия Deflate Stream в другом приложении, отредактировать распакованные данные и снова сжать их.

Распаковка работает нормально, но не для данных, сжатых мной.

Проблема в том, что когда я делаю это: byte [] result = Compressor.Compress (Compressor.Decompress (sourceData));

длина результирующего байтового массива отличается от массива sourceData.

Например:

    string source = "test value";
    byte[] oryg = Encoding.Default.GetBytes(source);

    byte[] comp = Compressor.Compress(oryg);
    byte[] result1 = Compressor.Decompress(comp);

    string result2 = Encoding.Default.GetString(res);

и здесь result1.Length равно 0, а result2 равно ""

Вот код моего класса Compressor.

public static class Compressor
{
    public static byte[] Decompress(byte[] data)
    {
        byte[] result;

        using (MemoryStream baseStream = new MemoryStream(data))
        {
            using (DeflateStream stream = new DeflateStream(baseStream, CompressionMode.Decompress))
            {
                result = ReadFully(stream, -1);
            }
        }

        return result;
    }

    public static byte[] Compress(byte[] data)
    {
        byte[] result;

        using (MemoryStream baseStream = new MemoryStream())
        {
            using (DeflateStream stream = new DeflateStream(baseStream, CompressionMode.Compress, true))
            {
                stream.Write(data, 0, data.Length);
                result = baseStream.ToArray();
            }
        }

        return result;
    }

    /// <summary>
    /// Reads data from a stream until the end is reached. The
    /// data is returned as a byte array. An IOException is
    /// thrown if any of the underlying IO calls fail.
    /// </summary>
    /// <param name="stream">The stream to read data from</param>
    /// <param name="initialLength">The initial buffer length</param>
    private static byte[] ReadFully(Stream stream, int initialLength)
    {
        // If we've been passed an unhelpful initial length, just
        // use 32K.
        if (initialLength < 1)
        {
            initialLength = 65768 / 2;
        }

        byte[] buffer = new byte[initialLength];
        int read = 0;

        int chunk;
        while ((chunk = stream.Read(buffer, read, buffer.Length - read)) > 0)
        {
            read += chunk;

            // If we've reached the end of our buffer, check to see if there's
            // any more information
            if (read == buffer.Length)
            {
                int nextByte = stream.ReadByte();

                // End of stream? If so, we're done
                if (nextByte == -1)
                {
                    return buffer;
                }

                // Nope. Resize the buffer, put in the byte we've just
                // read, and continue
                byte[] newBuffer = new byte[buffer.Length * 2];
                Array.Copy(buffer, newBuffer, buffer.Length);
                newBuffer[read] = (byte)nextByte;
                buffer = newBuffer;
                read++;
            }
        }
        // Buffer is now too big. Shrink it.
        byte[] ret = new byte[read];
        Array.Copy(buffer, ret, read);
        return ret;
    }
}

Пожалуйста, помогите мне с этим делом, если сможете. С наилучшими пожеланиями, Адам

1 Ответ

3 голосов
/ 03 октября 2010

(отредактировано: переключено с использования сброса, которое все еще может не сбрасывать все байты, на теперь обеспечение того, чтобы сначала выкачать дефлят, согласно ответу Фила здесь: zip и распаковать строку с Deflate )

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

public static byte[] Compress(byte[] data)
{
    byte[] result;

    using (MemoryStream baseStream = new MemoryStream())
    {
        using (DeflateStream stream = new DeflateStream(baseStream, CompressionMode.Compress, true))
        {
            stream.Write(data, 0, data.Length);
        }
        result = baseStream.ToArray();  // only safe to read after deflate closed
    }

    return result;
}    

Кроме того, ваша процедура ReadFully выглядит невероятно сложной и может содержать ошибки.Один из них:

while ((chunk = stream.Read(buffer, read, buffer.Length - read)) > 0)

При чтении 2-го блока, read будет больше, чем длина буфера, что означает, что он всегда будет передавать отрицательное значение в stream.Read для количества байтов вчитать.Я предполагаю, что он никогда не будет читать 2-й блок, возвращая ноль и выпадая из цикла while.

Я рекомендую для этого версию Джона ReadFully: Создание байтового массива из потока

...