为什么我不能使用operator>>读取fstream的二进制数据?

18

如果我做以下操作:

ifstream file;
file.open("somefile", ios::binary);

unsigned int data;

file >> data;

我的流(stream)将始终设置 failbit,并且 data 将保持未初始化状态。 然而,如果我读取一个 charunsigned char,则流就没问题了。 perror() 告诉我“result too large”。

我在 Google 上唯一看到的建议是不应该使用 operator>> 用于二进制数据(最好使用 read()),但我发现这个操作符更清晰、更易于使用,并且不需要对所有内容进行强制转换。

有人能解释一下这个问题吗?

2个回答

15
iostream提取运算符(>>)尝试解释由空格分隔的数字字符串,而不是二进制数据。有多种不同的方法可以以二进制形式编码无符号整数(例如,在little-endian字节顺序中使用32位2's补码表示)。这就是为什么必须使用read/write函数来操作这样的二进制缓冲区的原因。

然而,没有任何东西阻止你使用插入和提取运算符来实现自己的类,以便以任何形式对二进制数据进行序列化。这样的类可能会在内部使用ifstream对象的读取函数。或者,boost序列化库可能已经包含了您想要的内容。


1

应该按照您所描述的方式完成。然而,C++标准设计者并不是非常优雅。事实上,C++的设计存在许多缺陷,即使是C++11和C++14也有很多缺陷。

理想的C++设计应该是:

1.对于文本文件:

ifstream fin_txt("input.txt");
int i;
float j;
double k;
fin_txt >> i >> j >> k;

这段代码将读取3个字符串并解析为整数、浮点数和双精度浮点数,然后分别存储到i、j和k中。

2.对于二进制文件:

ifstream fin_txt("input.bin", ios::binary);
int i;
float j;
double k;
fin_txt >> i >> j >> k;

这将读取4/8字节(取决于int是32位还是64位)、4字节和8字节的二进制数据,并将它们分别存储到i、j和k中。

不幸的是,当前设计是对Case 2报告错误。也许在C++22中可以实现这一点。


你在回答中已经解释了为什么会这样:“取决于int是32位还是64位”。目前,使用<iostreams>的代码是可移植的,而你的提议则不是。 - Caleth
对于@Caleth,正如我所说的,这是标准C++中的另一个设计缺陷,它应该区分int32和int64而不仅仅是int,因为int可以是32位或64位,未来可能是128位。 int的字段大小确定性与其他类型不同(char = 8位,long = 32位,long long = 64位,float = 32位,double = 64位)。 要解决这个问题,我们可以简单地分别定义int32和int64,并且如果给定二进制文件的字段大小不确定类型,则会抛出运行时错误,即 fin_txt >> i; // 抛出错误, fin_txt >> (int32)i; // 编译正确 - xuancong84
这不是 ios::binary(和 ios::text)的作用。一些平台在处理带有行结束字符的文件时区分“文本模式”和“二进制模式”。ios 标志仅处理此类情况。 - Caleth
1
@Caleth 那么可能会有一个使用文本格式和二进制流完成的操作符>>和<<的文本流,另一个类则使用>>进行二进制写入。 - Adam

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接