Здравствуйте! Возникла проблема при использовании System.IO.Compression.GZipStream: Сжатие потока происходит корректно, а вот распаковка никак не удаётся.
Вот код для сжатия:
begin
Var Compressed:=System.IO.File.Create('Compressed.gz');
Var C:=new GZipStream(Compressed, System.IO.Compression.CompressionLevel.Fastest);
(System.IO.File.OpenRead('Data.dat')).CopyTo(C);
end.
Как распаковать такой файл? При этом для распаковки не желательно использовать внешний файл(для распакованных данных).
begin
var Compressed := System.IO.File.Create('Compressed.gz');
var C := new System.IO.Compression.GZipStream(Compressed, System.IO.Compression.CompressionLevel.Fastest);
(System.IO.File.OpenRead('json.txt')).CopyTo(C);
Compressed.Flush;
end.
Он и не должен читаться обычным архиватором. Это просто данные, сжатые по определённому алгоритму, не содержащие данных о исходном файле. То есть сжать так несколько файлов невозможно. Для распаковки используется GZipStream.
Надо использовать C.Flush, похоже, вместо Compressed.Flush. Теперь хотя бы запаковалось. И кстати архиватор всё ещё читает этот файл без проблем. Ну разве что имя и расширение отсутствуют)). Ну то есть, я так понял, и все данные типа жанра .mp3 файлов тоже сотрёт.
function ReadGZip(C: System.IO.Compression.GZipStream): sequence of byte;
begin
while true do
begin
var i := C.ReadByte;
if i = -1 then break;
yield i;
end;
end;
begin
var Compressed := System.IO.File.Create('Compressed.gz');
var C := new System.IO.Compression.GZipStream(Compressed, System.IO.Compression.CompressionLevel.Fastest);
(System.IO.File.OpenRead('json.txt')).CopyTo(C);
C.Close;
Compressed.Close;
Compressed := System.IO.File.OpenRead('Compressed.gz');
C := new System.IO.Compression.GZipStream(Compressed, System.IO.Compression.CompressionMode.Decompress);
var a := ReadGZip(C).ToArray;
System.IO.File.WriteAllBytes('json copy.txt', a);
end.
Вроде всё понял, но что такое yield? А правильность файла после распаковки проверяли? То есть, совпадает ли содержимое распакованного файла и оригинала.
Фунции с yield это особые функции для создания последовательностей - алгоритмов. К примеру:
function f1:sequence of byte;
begin
loop 10 do yield Random(256);
end;
begin
var seq := f1();
writeln(seq);
writeln(seq);
end.
Выпишет 2 разных массива, потому что в seq хранится только алгоритм. Ну а в данном случае я использовал это только потому что заранее не известно количество байт. Я не уверен быстрее ли это чем список, но хотя бы удобнее. Почитайте справку про yield и yield sequence.
И да, я проверил - распаковка дала точную копию исходного файла.
Спасибо! Если что, то это нужно для оптимизации хранения данных небольшой нейросети для распознавания рукописных цифр. Как доделаю - смогу здесь показать.
Нет, я декомпилировал - ToArray в данном случае просто перебирает всю последовательность до конца, с помощью foreach и добавляет каждый элемент в списк-о-подобный класс (алгоритм добавления элементов тот же что и в List). Так только много лишних объектов создаёт и уничтожает (функция с yield это на самом деле тоже класс, она превращается в function f1:sequence of byte := new clyield#1, а потом ещё много классов - обёрток передают друг другу своё содержимое, при вызове ToArray).
Объём данных у меня небольшой, порядка 40-45 КБ, поэтому скорость выполнения этой части кода будет составлять единицы миллисекунд. А вообще, благодаря Вашему примеру я нашёл у себя в коде ошибку, из-за которой распаковка происходила некорректно. Проблема проста: нужно всего лишь закрывать потоки.