如何在C ++中读取复数的二进制文件

时间:2016-11-03 01:58:16

标签: c++ matlab

我有一个320Mb的二进制文件(data.dat),包含32e7行十六进制数字:

1312cf60 d9 ff e0 ff 05 00 f0 ff 22 00 2f 00 fe ff 33 00 |........"./...3.|
1312cf70 00 00 00 00 f4 ff 1d 00 3d 00 6d 00 53 00 db ff |........=.m.S...|
1312cf80 b7 ff b0 ff 1e 00 0c 00 67 00 d1 ff be ff f8 ff |........g.......|
1312cf90 0b 00 6b 00 38 00 f3 ff cf ff cb ff e4 ff 4b 00 |..k.8.........K.|
....

原始数字是:

(16,-144)
(-80,-64)
(-80,16)
(16,48)
(96,95)
(111,-32)
(64,-96)
(64,-16)
(31,-48)
(-96,-48)
(-32,79)
(16,48)
(-80,80)
(-48,128)
...

我有一个matlab代码,可以将它们作为实数读取并将它们转换为复数:

nsamps = (256*1024);
for i = 1:305
     nstart = 1 + (i - 1) * nsamps ;
     fid = fopen('data.dat');
     fseek(fid,4 * nstart ,'bof');
     y = fread(fid,[2,nsamps],'short');
     fclose(fid);
     x = complex(y(1,:),y(2,:));

我正在使用C ++并尝试将数据作为vector<complex<float>>

获取
std::ifstream in('data.dat', std::ios_base::in | std::ios_base::binary);
fseek(infile1, 4*nstart, SEEK_SET);
vector<complex<float> > sx;
in.read(reinterpret_cast<char*>(&sx), sizeof(int));

非常混淆使用C ++获取复杂数据。谁能给我一个帮助?

1 个答案:

答案 0 :(得分:0)

理论

我将尝试使用代码中的问题作为示例解释一些要点。

让我们从代码的结尾开始。您尝试读取一个数字,该数字存储为四字节单精度floating point number,但您使用sizeof(int)作为大小参数。在现代x86平台上,现代编译器sizeof(int)往往等于sizeof(float),但不能保证。 sizeof(int)取决于编译器,因此请改用sizeof(float)

在matlab代码中,您读取2*nsamps个数字,而在C ++代码中,只读取了四个字节(一个数字)。像sizeof(float) * 2 * nsamps这样的东西会更接近matlab代码。

接下来,std::complex是一个复杂的类,(通常)可能有任何实现定义的内部表示。但幸运的是,here我们读到了

  

对于z类型的任何对象complex<T>reinterpret_cast<T(&)[2]>(z)[0]   是z的真实部分,而reinterpret_cast<T(&)[2]>(z)[1]z   complex<T>的想象部分。

     

对于任何指向名为p的{​​{1}}数组元素的指针   任何有效的数组索引ireinterpret_cast<T*>(p)[2*i]都是真实的部分   复数p[i]reinterpret_cast<T*>(p)[2*i + 1]是   复数p[i]的虚部。

所以我们可以将std::complex转换为char类型并在那里读取二进制数据。但是std::vector是一个类模板,它的实现定义的内部表示也是如此!这意味着,我们不能只是reinterpret_cast<char*>(&sx)并将二进制数据写入指针,因为它指向向量对象的开头,这不太可能是向量的开头数据。现代C ++获取数据开头的方法是调用sx.data()。 Pre-C ++ 11方法是获取第一个元素的地址:&sx[0]。从头开始覆盖对象几乎总是会导致段错误。

好的,现在我们有数据缓冲区的开头,它能够接收复数的二进制表示。但是当你声明vector<complex<float> > sx;时,它的大小为零,并且由于你不是pushingemplacing它的元素,向量将不会“知道”它应该调整大小。又是Segfault。所以只需致电resize

sx.resize(number_of_complex_numbers_to_store);

或使用适当的构造函数:

vector<complex<float> > sx(number_of_complex_numbers_to_store);

在将数据写入向量之前。请注意,这些方法使用存储元素数量的“高级”概念,而不是要存储的字节数。

总而言之,代码的最后两行应如下所示:

vector<complex<float> > sx(nsamps);
in.read(reinterpret_cast<char*>(sx.data()), 2 * nsamps * sizeof(float));

最小的例子

如果您继续遇到麻烦,请先尝试更简单的沙箱代码。

例如,让我们将六个float写入二进制文件:

std::ofstream ofs("file.dat", std::ios::binary | std::ios::out);
float foo[] = {1,2,3,4,5,6};
ofs.write(reinterpret_cast<char*>(foo), 6*sizeof(float));
ofs.close();

然后将它们读成复杂的载体:

std::ifstream ifs("file.dat", std::ios::binary | std::ios::in);
std::vector<std::complex<float>> v(3);
ifs.read(reinterpret_cast<char*>(v.data()), 6*sizeof(float));
ifs.close();

最后打印出来:

std::cout << v[0] << " " << v[1] << " " << v[2] << std::endl;

程序打印:

(1,2) (3,4) (5,6)

所以这种方法很好。

二进制文件

以下是关于二进制文件的评论,我最初将其作为评论发布。

二进制文件没有“行”的概念。二进制文件中“行”的数量完全取决于您正在查看的窗口的大小。将二进制文件视为磁带,其中磁头的每个离散位置只能读取一个字节。解释这些字节取决于你。

如果一切正常,但你得到奇怪的数字,请检查fseek电话中的位移。多个字节的错误会产生随机值,而不是您希望获得的浮点值。

当然,您可能只是读取float s的向量(或数组),观察上述注意事项,然后将它们转换为循环中的复数。此外,这是调试fseek电话的好方法,以确保您从正确的位置开始阅读。

相关问题