Чтение сжатого файла и запись в новый файл не допускает распаковку - PullRequest
3 голосов
/ 13 июля 2011

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

Программа сжимает данные в файл с помощью GZipStream.Полученный сжатый файл будет C: \ mydata.dat .

. Затем я читаю этот файл и записываю его в новый файл.

//Read original file
string compressedFile = String.Empty;
using (StreamReader reader = new StreamReader(@"C:\mydata.dat"))
{
    compressedFile = reader.ReadToEnd();
    reader.Close();
    reader.Dispose();
}

//Write to a new file
using (StreamWriter file = new StreamWriter(@"C:\mynewdata.dat"))
{
    file.WriteLine(compressedUserFile);
}

Когда я пытаюсьчтобы распаковать два файла, исходный распаковывается идеально, но новый файл вызывает InvalidDataException с сообщением Магическое число в заголовке GZip неверно.Убедитесь, что вы передаете поток GZip.

Почему эти файлы отличаются?

Ответы [ 2 ]

3 голосов
/ 13 июля 2011

StreamReader предназначен для чтения последовательности символов , а не байтов. То же относится и к StremWriter. Поскольку обработка сжатых файлов как потока символов не имеет никакого смысла, вам следует использовать некоторую реализацию Stream. Если вы хотите получить поток в виде массива байтов, вы можете использовать MemoryStream.

Точная причина, по которой использование символьных потоков не работает, заключается в том, что они принимают кодировку UTF-8 по умолчанию. Если какой-то байт является недопустимым UTF-8 (например, второй байт заголовка, 0x8B), он представляется как «символ замены» Unicode (U + FFFD). Когда строка записывается обратно, этот символ кодируется с использованием UTF-8 во что-то совершенно отличное от того, что было в источнике.

Например, чтобы прочитать файл из потока, получить его в виде массива байтов, а затем записать его в другие файлы в виде потока:

byte[] bytes;
using (var fileStream = new FileStream(@"C:\mydata.dat", FileMode.Open))
using (var memoryStream = new MemoryStream())
{
    fileStream.CopyTo(memoryStream);
    bytes = memoryStream.ToArray();
}

using (var memoryStream = new MemoryStream(bytes))
using (var fileStream = new FileStream(@"C:\mynewdata.dat", FileMode.Create))
{
    memoryStream.CopyTo(fileStream);
}

Метод CopyTo() доступен только в .Net 4, но вы можете написать свой , если используете более старые версии.

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

byte[] bytes = File.ReadAllBytes(@"C:\mydata.dat");
File.WriteAllBytes(@"C:\mynewdata.dat", bytes);
0 голосов
/ 13 июля 2011

РЕДАКТИРОВАТЬ: По-видимому, мои предложения неверны / недействительны / что угодно ... пожалуйста, используйте один из других, которые, без сомнения, были сильно пересмотрены до такой степени, что не может быть достигнута дополнительная производительность (иначе, это будет значит, они так же недействительны, как и я)

using (System.IO.StreamReader sr = new System.IO.StreamReader(@"C:\mydata.dat"))
{
    using (System.IO.StreamWriter sw = new System.IO.StreamWriter(@"C:\mynewdata.dat"))
    {
        byte[] bytes = new byte[1024];
        int count = 0;
        while((count = sr.BaseStream.Read(bytes, 0, bytes.Length)) > 0){
            sw.BaseStream.Write(bytes, 0, count);
        }
    }
}

Читать все байты

byte[] bytes = null;
using (System.IO.StreamReader sr = new System.IO.StreamReader(@"C:\mydata.dat"))
{
    bytes = new byte[sr.BaseStream.Length];
    int index = 0;
    int count = 0;
    while((count = sr.BaseStream.Read(bytes, index, 1024)) > 0){
        index += count;
    }
}

Прочитать все байты / записать все байты (из ответа svick):

byte[] bytes = File.ReadAllBytes(@"C:\mydata.dat");
File.WriteAllBytes(@"C:\mynewdata.dat", bytes);

ПРОВЕРКА РАБОТЫ С ДРУГИМИ ОТВЕТАМИ:

Только что сделал быстрый тест между моим Ответом (StreamReader) (первая часть выше, копия файла) и ответом svick (FileStream / MemoryStream) (первый). Тест состоит из 1000 итераций кода, здесь приведены результаты 4 тестов (результаты в целых секундах, все фактические результаты немного превышают эти значения):

My Code | svick code
--------------------
9       | 12
9       | 14
8       | 13
8       | 14

Как вы можете видеть, по крайней мере, в моем тесте мой код работал лучше. Одна вещь, которую стоит заметить в моем случае, это то, что я не читаю поток символов, на самом деле я обращаюсь к BaseStream, который предоставляет поток байтов. Возможно, ответ Свика медленный, потому что он использует два потока для чтения, затем два для записи. Конечно, есть много способов оптимизации ответа svick для повышения производительности (и он также предоставил альтернативу для простого копирования файлов)

Тестирование с третьим вариантом (ReadAllBytes / WriteAllBytes)

My Code | svick code | 3rd
----------------------------
8       | 14         | 7
9       | 18         | 9
9       | 17         | 8
9       | 17         | 9

Примечание: в миллисекундах третий вариант всегда был лучше

...