Простой рабочий пример GZIPOutputStream и GZIPInputStream с буферами протокола

#c #gzip #iostream #protocol-buffers

#c #gzip #iostream #протокол-буферы

Вопрос:

после нескольких дней экспериментов с буферами протокола я попытался сжать файлы. С Python это довольно просто сделать и не требует никакой игры с потоками.

Поскольку большая часть нашего кода написана на C , я хотел бы сжимать / распаковывать файлы на том же языке. Я пробовал библиотеку boost gzip, но не смог заставить ее работать (без сжатия):

 int writeEventCollection(HEP::MyProtoBufClass* protobuf, std::string filename, unsigned int compressionLevel) {
            ofstream file(filename.c_str(), ios_base::out | ios_base::binary);
            filtering_streambuf<output> out;
            out.push(gzip_compressor(compressionLevel));
            out.push(file);
            if (!protobuf->SerializeToOstream(amp;file)) {//serialising to wrong stream I asume
                    cerr << "Failed to write ProtoBuf." << endl;
                    return -1;
            }
            return 0;
    }
  

Я искал примеры использования GZIPOutputStream и GZIPInputStream с буферами протокола, но не смог найти рабочий
пример.

Как вы, наверное, уже заметили, я в лучшем случае новичок в потоках и был бы очень признателен за полностью рабочий пример, как в http://code.google.com/apis/protocolbuffers/docs/cpptutorial.html (У меня есть моя адресная книга, как мне сохранить ее в gziped-файле?)

Заранее благодарю.

РЕДАКТИРОВАТЬ: Рабочие примеры.

Пример 1 после ответа здесь на StackOverflow

 int writeEventCollection(shared_ptr<HEP::EventCollection> eCollection, 
std::string filename, unsigned int compressionLevel) { 
filtering_ostream out; 
out.push(gzip_compressor(compressionLevel)); 
out.push(file_sink(filename, ios_base::out | ios_base::binary)); 
if (!eCollection->SerializeToOstream(amp;out)) { 
                cerr << "Failed to write event collection." << endl; 
                return -1; 
} 

return 0; 
} 
  

Пример 2 следующий ответ в группе обсуждения Protobuf в Google:

 int writeEventCollection2(shared_ptr<HEP::EventCollection> 
eCollection, std::string filename, 
                        unsigned int compressionLevel) { 
using namespace google::protobuf::io; 
int filedescriptor = open(filename.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 
                S_IREAD | S_IWRITE); 
if (filedescriptor == -1) { 
                        throw "open failed on output file"; 
                } 
google::protobuf::io::FileOutputStream file_stream(filedescriptor); 
GzipOutputStream::Options options; 
options.format = GzipOutputStream::GZIP; 
options.compression_level = compressionLevel; 
google::protobuf::io::GzipOutputStream gzip_stream(amp;file_stream, 
options); 
if (!eCollection->SerializeToZeroCopyStream(amp;gzip_stream)) { 
     cerr << "Failed to write event collection." << endl; 
     return -1; 
     } 
close(filedescriptor); 
return 0; 
} 
  

Некоторые комментарии по производительности (чтение текущего формата и запись файлов ProtoBuf 11146):
Пример 1:

 real    13m1.185s 
user    11m18.500s 
sys     0m13.430s 
CPU usage: 65-70% 
Size of test sample: 4.2 GB (uncompressed 7.7 GB, our current compressed format: 7.7 GB)
  

Пример 2:

 real    12m37.061s 
user    10m55.460s 
sys     0m11.900s 
CPU usage: 90-100% 
Size of test sample: 3.9 GB
  

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

Комментарии:

1. Обратите внимание, что основная проблема здесь не связана с буферами протокола — любой пример «write / chain gzip через поток» должен работать.

Ответ №1:

Ваше предположение верно: опубликованный вами код не работает, потому что вы пишете непосредственно в ofstream , а не через filtering_streambuf . Чтобы сделать эту работу, вы можете использовать filtering_ostream вместо:

 ofstream file(filename.c_str(), ios_base::out | ios_base::binary); 
filtering_ostream out; 
out.push(gzip_compressor(compressionLevel)); 
out.push(file);

if (!protobuf->SerializeToOstream(amp;out)) {
    // ... etc.
}
  

Или более кратко, используя file_sink :

 filtering_ostream out; 
out.push(gzip_compressor(compressionLevel)); 
out.push(file_sink(filename, ios_base::out | ios_base::binary));

if (!protobuf->SerializeToOstream(amp;out)) {
    // ... etc.
}
  

Надеюсь, это поможет!