Зная текущий размер сжатого файла, используя gzwrite (zlib) - PullRequest
0 голосов
/ 01 ноября 2018

Я использую zlib для c ++.

Цитата из http://refspecs.linuxbase.org/LSB_3.0.0/LSB-PDA/LSB-PDA/zlib-gzwrite-1.html относительно gzwrite функции:

Функция gzwrite() должна записывать данные в сжатый файл со ссылкой file, который должен быть открыт в режиме записи (см. gzopen() и gzdopen()). При вводе buf должен указывать на буфер, содержащий len байтов несжатых данных. Функция gzwrite() должна сжать эти данные и записать их в файл. Функция gzwrite() возвращает количество фактически записанных несжатых байтов.

Я интерпретирую это, поскольку возвращаемое значение НЕ скажет мне, насколько больше размер файла при записи Только сколько данных было сжато в файл.

Единственный способ узнать размер файла - закрыть его и прочитать размер из файловой системы. У меня есть требование только продолжать запись в файл, пока он не достигнет определенного размера. Можно ли этого достичь, не закрывая файл?

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

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

Есть ли лучшие варианты?

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

Ответы [ 2 ]

0 голосов
/ 01 ноября 2018

zlib предоставляет функцию gzoffset(), которая делает именно то, что вы просите.

Если по какой-то причине вы застряли с версией zlib, которой более восьми лет, когда был добавлен gzoffset(), то это легко сделать с gzdopen(). Вы открываете выходной файл с помощью fopen() или open() и предоставляете дескриптор файла (используя fileno() и dup(), если вы использовали fopen()), а затем предоставляете этот дескриптор для gzdopen(). Затем вы можете использовать ftell() или lseek() в любое время, чтобы увидеть, сколько написано. Будьте осторожны, чтобы не пытаться дважды закрыть дескриптор. Смотрите комментарии для gzdopen().

0 голосов
/ 01 ноября 2018

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

Для настройки необходимо сначала открыть файл для записи с помощью простого open. Затем создайте канал через pipe2 и инициализируйте zlib, передав один из дескрипторов канала в gzdopen:

int out = open("/path/to/file", O_WRONLY | O_CREAT | O_TRUNC);
int p[2];
pipe2(p, O_NONBLOCK);
gzFile zFile = gzdopen(p[0], "w");

Теперь вы можете сначала записать данные в канал, а затем соединить их из канала в выходной файл:

gzwrite(zFile, buf, 1024); //or any other length
size_t bytesWritten = 0;
do {
    bytesWritten = splice(p[1], NULL, out, NULL, 1024, SPLICE_F_NONBLOCK | SPLICE_F_MORE);
} while(bytesWritten == 1024);

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

Замечание о сплайсинге: Сплайс специфичен для Linux и в основном является очень эффективной копией. Вы всегда можете заменить его простой комбинацией «чтение и запись», то есть прочитать данные из fd[1] в буфер, а затем записать данные из этого буфера в out - сращивание выполняется быстрее и меньше кода.

...