2016-05-19 6 views
4

Я хочу написать массив в файл, сжимая его, когда я иду.Чтение и запись массива в сжатый файл с boost iostreams

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

Boost's Iostreams выглядит как хороший способ, поэтому я построил следующий код. К сожалению, выходные и входные данные не сравниваются в конце. Но они очень близки:

Output   Input 
0.8401877284 0.8401880264 
0.3943829238 0.3943830132 
0.7830992341 0.7830989957 
0.7984400392 0.7984399796 
0.9116473794 0.9116470218 
0.1975513697 0.1975509971 
0.3352227509 0.3352229893 

Это говорит о том, что младший байт каждого поплавка изменяется или что-то в этом роде. Однако сжатие должно быть без потерь, поэтому это не ожидается или не будет желательным. Что дает?

//Compile with: g++ test.cpp --std=c++11 -lz -lboost_iostreams 
#include <fstream> 
#include <iostream> 
#include <boost/iostreams/filtering_stream.hpp> 
#include <boost/iostreams/filter/zlib.hpp> 
#include <cstdlib> 
#include <vector> 
#include <iomanip> 

int main() 
{ 
    using namespace std; 
    using namespace boost::iostreams; 

    const int NUM = 10000; 

    std::vector<float> data_out; 
    std::vector<float> data_in; 
    data_in.resize(NUM); 
    for(float i=0;i<NUM;i++) 
     data_out.push_back(rand()/(float)RAND_MAX); 

    { 
     ofstream file("/z/hello.z", ios_base::out | ios_base::binary); 
     filtering_ostream out; 
     out.push(zlib_compressor()); 
     out.push(file); 

     for(const auto d: data_out) 
     out<<d; 
    } 

    { 
     ifstream file_in("hello.z", ios_base::in | ios_base::binary); 
     filtering_istream in; 
     in.push(zlib_decompressor()); 
     in.push(file_in); 

     for(float i=0;i<NUM;i++) 
     in>>data_in[i]; 
    } 

    bool all_good=true; 
    for(int i=0;i<NUM;i++){ 
     cout<<std::setprecision(10)<<data_out[i]<<" "<<data_in[i]<<endl; 
     all_good &= (data_out[i]==data_in[i]); 
    } 

    cout<<"Good? "<<(int)all_good<<endl; 
} 

И, да, я очень предпочитаю использовать операторы потока так, как я делаю, а не толкать или тянуть весь вектор блок сразу.

+0

Возникает ли проблема, если вы оставите компрессию из образца? –

+1

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

+0

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

ответ

1

Как указал Дэн Машек в редакторе their answer, оператор потока <<, который я использовал, преобразовывал мои данные с плавающей запятой в текстовое представление перед сжатием. По какой-то причине я этого не ожидал.

Использование библиотеки сериализации - один из способов избежать этого, но в дополнение к возможным возможностям overhead ввести дополнительные зависимости.

Поэтому я использовал reinterpret_cast для данных с плавающей запятой и метод ostream::write() для записи данных без преобразования по одному символу за раз. Чтение использует аналогичный метод.Эффективность может быть улучшена за счет увеличения количества символов, записанных за раз.

#include <fstream> 
#include <iostream> 
#include <boost/iostreams/filtering_stream.hpp> 
#include <boost/iostreams/filter/zlib.hpp> 
#include <cstdlib> 
#include <vector> 
#include <iomanip> 

int main() 
{ 
    using namespace std; 
    using namespace boost::iostreams; 

    const int NUM = 10000; 

    std::vector<float> data_out; 
    std::vector<float> data_in; 
    data_in.resize(NUM); 
    for(float i=0;i<NUM;i++) 
     data_out.push_back(233*(rand()/(float)RAND_MAX)); 

    { 
     ofstream file("/z/hello.z", ios_base::out | ios_base::binary); 
     filtering_ostream out; 
     out.push(zlib_compressor()); 
     out.push(file); 

     char *dptr = reinterpret_cast<char*>(data_out.data()); 

     for(int i=0;i<sizeof(float)*NUM;i++) 
     out.write(&dptr[i],1); 
    } 

    { 
     ifstream file_in("hello.z", ios_base::in | ios_base::binary); 
     filtering_istream in; 
     in.push(zlib_decompressor()); 
     in.push(file_in); 

     char *dptr = reinterpret_cast<char*>(data_in.data()); 

     for(int i=0;i<sizeof(float)*NUM;i++) 
     in.read(&dptr[i],1); 
    } 

    bool all_good=true; 
    for(int i=0;i<NUM;i++){ 
     cout<<std::setprecision(10)<<data_out[i]<<" "<<data_in[i]<<endl; 
     all_good &= (data_out[i]==data_in[i]); 
    } 

    cout<<"Good? "<<(int)all_good<<endl; 
} 
+0

Просто из любопытства, на чем вы основываете вывод, что использование boost :: serialization удвоит требования к памяти? По моему мнению, он пишет в основной поток почти так же, как и вы. Во всяком случае, влияние использования zlib на ваш объем памяти будет значительно более значительным (для всех его внутренних структур данных). –

+0

Неплохо, прошло некоторое время с тех пор, как я работал с сериализацией. Когда я это сделал, сериализованное представление нужно было сохранить в памяти, что удвоило требование. Я полагаю, что, вероятно, есть способ использовать сериализацию потоковым способом. Я перешел от сериализации Boost, но к Cereal, чтобы уменьшить зависимости и скорость ([link] (https://github.com/thekvs/cpp-serializers)). – Richard

2

Проблема не в сжатии, а в том, как вы сериализуете значения вектора.

Если отключить сжатие и ограничить размер до 10 элементов для упрощения проверки, вы можете увидеть, что полученный файл выглядит примерно так:

0.001251260.5635850.1933040.808740.5850090.4798730.3502910.8959620.822840.746605 

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

Это происходит, поскольку вы используете stream operator <<, который форматирует числовые типы в виде текста.


Самое простое решение, казалось бы, используя подталкивания :: сериализациями для обработки чтения и записи (и использовать повышение :: iostreams в качестве базового потока сжатого). Я использовал двоичный архив, но вы также могли бы использовать текстовый архив (просто замените binary_ на text_).

Пример кода:

#include <fstream> 
#include <iostream> 
#include <boost/iostreams/filtering_stream.hpp> 
#include <boost/iostreams/filter/zlib.hpp> 

#include <boost/archive/binary_oarchive.hpp> 
#include <boost/archive/binary_iarchive.hpp> 
#include <boost/serialization/vector.hpp> 

#include <cstdlib> 
#include <vector> 
#include <iomanip> 

int main() 
{ 
    using namespace std; 
    using namespace boost::iostreams; 

    const int NUM = 10; 

    std::vector<float> data_out; 
    for (float i = 0; i < NUM; i++) { 
     data_out.push_back(rand()/(float)RAND_MAX); 
    } 

    { 
     ofstream file("hello.z", ios_base::out | ios_base::binary); 
     filtering_ostream out; 
     out.push(zlib_compressor()); 
     out.push(file); 

     boost::archive::binary_oarchive oa(out); 
     oa & data_out; 
    } 

    std::vector<float> data_in; 
    { 
     ifstream file_in("hello.z", ios_base::in | ios_base::binary); 
     filtering_istream in; 
     in.push(zlib_decompressor()); 
     in.push(file_in); 

     boost::archive::binary_iarchive ia(in); 
     ia & data_in; 
    } 

    bool all_good=true; 
    for(int i=0;i<NUM;i++){ 
     cout<<std::setprecision(10)<<data_out[i]<<" "<<data_in[i]<<endl; 
     all_good &= (data_out[i]==data_in[i]); 
    } 

    cout<<"Good? "<<(int)all_good<<endl; 
} 

Консоль вывода:

0.001251258887 0.001251258887 
0.563585341 0.563585341 
0.1933042407 0.1933042407 
0.8087404966 0.8087404966 
0.5850093365 0.5850093365 
0.4798730314 0.4798730314 
0.3502914608 0.3502914608 
0.8959624171 0.8959624171 
0.822840035 0.822840035 
0.7466048002 0.7466048002 
Good? 1 

Незначительная проблема заключается в том, что вы не сериализовать размер вектора, поэтому при чтении вы должны продолжать читать до конца потока.

+0

Спасибо, Дэн. Твое замечательное наблюдение было достаточным для того, чтобы я мог понять все остальное. Я не уверен, почему я ожидал, что оператор '<<' не сделает преобразование. – Richard

 Смежные вопросы

  • Нет связанных вопросов^_^