我想在一个文件中写一个数组,然后按我的方式压缩它。
稍后,我想从该文件中读取数组,并在我去的时候对其进行解压缩。
Boost的Iostream似乎是一个很好的方法,所以我构建了以下代码。不幸的是,输出和输入数据在最后并不相等。但他们几乎做到了:
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 :(得分:4)
问题不在于压缩,而在于序列化向量值的方式。
如果禁用压缩并将大小限制为10个元素以便于检查,您可以看到生成的文件如下所示:
0.001251260.5635850.1933040.808740.5850090.4798730.3502910.8959620.822840.746605
如您所见,数字表示为文本,小数位数有限,没有分隔符。这是绝对的机会(因为您只使用值<1.0),您的程序能够产生一个远程合理的结果。
由于您使用将数字类型格式化为文本的stream operator <<
,因此会发生这种情况。
最简单的解决方案似乎是使用 boost :: serialization 来处理读写(并使用boost :: iostreams作为底层压缩流)。我使用了二进制存档,但您也可以使用文本存档(只需将binary_替换为文本_)。
示例代码:
#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
一个小问题是你没有序列化向量的大小,所以在阅读时你必须继续阅读直到流的结尾。
答案 1 :(得分:1)
正如DanMašek在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;
}