我有一个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 ++获取复杂数据。谁能给我一个帮助?
答案 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}}数组元素的指针 任何有效的数组索引i
,reinterpret_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;
时,它的大小为零,并且由于你不是pushing或emplacing它的元素,向量将不会“知道”它应该调整大小。又是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
电话的好方法,以确保您从正确的位置开始阅读。