chapter8_question2_book(what does rdstate() return value means?)
Closed this issue · 51 comments
@pezy
I asked this queation in SO.
尽管大牛回答了,依然不是太明白,再此以中文重新描述一下。
istream& Read(istream &is)
{
std::string buf;
while (is >> buf)
{
cout << is.eofbit << " " << is.failbit << " " << is.badbit << endl;
cout << is.rdstate() << endl;
cout << buf << endl;
}
cout << is.eofbit << " " << is.failbit << " " << is.badbit << endl;
cout << is.rdstate() << endl;
is.clear();
cout << is.eofbit << " " << is.failbit << " " << is.badbit << endl;
cout << is.rdstate() << endl;
return is;
}
我如果输入普通的字符串,输出是1 2 4 0,对应eofbit ailbit badbit rdstate()
如果我键入 CTRL+Z (windows),输出1 2 4 3,说明当前状态有变化,那么这个返回的3代表什么意思呢?是eofbit ailbit badbit 变化的个数?比如变化两个,就返回2?
2.我输入CTRL+Z,按说eofbit和failbit 会变化吧?书上都说了
Reaching end-of-file sets both eofbit and failbit
3.又例如我输入
std::istringstream in2("test test");
Read(in2 );
output is 1 2 4 0 test 1 2 4 1 test 1 2 4 3 1 2 4 0
中间输出个1我很诧异,好像没改变什么值,怎么返回的状态就改变了呢?
4.又例如我输入
std::istringstream in("test test");
Read(in >> std::noskipws);
output is 1 2 4 0 test 1 2 4 2 1 2 4 0
我又疑惑了: a.为何只输出一个test呢?加了noskipws,我觉得应该是直接将test test当作一个单词一次性输出的。 b.为什么这里返回的状态又变成2了?
上面的1 2 4我认为的意思是eofbit ailbit badbit在计算机中是连续排列的。
111 高位1代表badbit,低位1代表eofbit,所以在取它们值的时候,自动忽略其他位的值,所以取badbit就相当于100(4),eofbit相当于001(1)
以上
(最近忙于老师布置的作业,一个星期没看C++primer了,感觉拉下好多啊)
顺便@Mooophy 提高一下答题效率,再此先行谢过2位!
我们可以从多个角度来想办法,先看看源码有什么线索。
下面贴自 VS2013:
enum _Iostate
{ // constants for stream states
_Statmask = 0x17};
static const _Iostate goodbit = (_Iostate)0x0;
static const _Iostate eofbit = (_Iostate)0x1;
static const _Iostate failbit = (_Iostate)0x2;
static const _Iostate badbit = (_Iostate)0x4;
static const _Iostate _Hardfail = (_Iostate)0x10;
由于到2013为止,vs仍没有实现constexpr
,所以这里我们看到的是static const
。但我们仍能看出:
eofbit
、failbit
、badbit
都是静态的且只可读。换句话说,这些变量在程序编译之前已经定死,是不变的。
于是,思路和后续的实验代码都可以得到一定的简化。
另外,还可从上述看出:eofbit
等虽然以bit命名,但事实上并非以bit存储。
事实上在PC、服务器等机器上很少见直接进行bit存储和操作的。(个别单片机是可以的,此处不展开说了)
另外,观察 1,2,4这几个值。
特点是正好是2的 0,1, 2次方。这种数字有个特点,就是可以保证相加之后结果的唯一性。比如
3 = 1 + 2
于是3 就代表 1 和 2 两个状态同时存在,回到 @Ocxs 第一个疑问:
我如果输入普通的字符串,输出是1 2 4 0,对应eofbit ailbit badbit rdstate()
如果我键入 CTRL+Z (windows),输出1 2 4 3,说明当前状态有变化,那么这个返回的3代表什么意思呢?是eofbit ailbit badbit 变化的个数?比如变化两个,就返回2?
此处的3应该代表 1 + 2,即eofbit
和 failbit
对应值的相加,代表二者同时存在。也就是你在疑问2里所提到的:
Reaching end-of-file sets both eofbit and failbit
关于疑问3。可以从另外一个角度入手,推敲一下实验代码,有没有漏掉重要信息。个人觉得,目前的实验代码不够细,关键细节从眼皮底下溜走了。
现修改如下:
#include <iostream>
#include <istream>
#include <string>
#include <sstream>
using namespace std;
istream& Read(istream &is)
{
int i;
std::string buf;
int count = 1;
while (std::cout << "\nwhile checking " << count++ << " --> ", is >> buf)
{
cout << is.eofbit << " " << is.failbit << " " << is.badbit << endl;
cout << is.rdstate() << endl;
cout << buf << endl;
}
cout << "\n\nout of while loop before clearing\n\n";
cout << is.eofbit << " " << is.failbit << " " << is.badbit << endl;
cout << is.rdstate() << endl;
cout << "\n\nout of while loop after clearing\n\n";
is.clear();
cout << is.eofbit << " " << is.failbit << " " << is.badbit << endl;
cout << is.rdstate() << endl;
return is;
}
int main()
{
std::istringstream in2("test test");
Read(in2);
system("pause");
return 0;
}
运行结果:
while checking 1 --> 1 2 4
0
test
while checking 2 --> 1 2 4
1
test
while checking 3 -->
out of while loop before clearing
1 2 4
3
out of while loop after clearing
1 2 4
0
Press any key to continue . . .
回到你的疑问:
...
中间输出个1我很诧异,好像没改变什么值,怎么返回的状态就改变了呢?
从上述代码和运行结果可知:
1、中间这个1
发生在第二次while
check时,eofbit
置位,于是输出在循环内部输出1
;
2、最后这个3
发生在第三次while
check时,这次check失败,跳出循环,同时failbit
置位。跳出循环后、clear之前,eofbit
和failbit
同时存在。如前所述,3 = 1 + 2, 故在循环外部输出3。
需要指出的是,eofbit
、failbit
等几个值的实现细节是machine dependent 的(我甚至怀疑这部分就不是standard规定范畴)。上述代码的运行环境是:windows 8.1 + vs2013。
我试了一下Ubuntu14.04+gcc4.82,运行结果是不一样的。
关于疑问4。
你的疑问是源出SO大牛@dietmar Kühl的 回复吧:
To get
std::ios_base::failbit
set without also havingstd::ios_base::eofbit
set you'll need to prevent it from seeing a non-whitespace character: the input operator forstd::string
by default start off skipping whitespace and then reads until it either reaches whitespace or EOF and it is successful if it could read at least one non-whitespace character. An approach to do that is to turn automatic skipping of whitespace off, e.g.:std::istringstream in("test test"); Read(in >> std::noskipws);
大牛的意思用中文重新整理如下:
如果你想只对failbit置位又想避免eofbit同时被置位的话,你可以制造一个逻辑矛盾。默认状况下:
std::string
在从stream读取的时候,会跳过开头的所有空格,直到读到非空格的字符或者EOF才算读取成功,否则视为读取失败并对failbit置位。因此,我们可以用下述代码制造逻辑矛盾:std::istringstream in("test test"); Read(in >> std::noskipws);
std::noskipws
的语义是:禁止忽略开头的空格(也包括tab什么的)。于是,就制造了一个语义上的矛盾:
1、is >> buf
要求跳掉开头的空格;
2、std::noskipws
要求处理开头的空格;
上述代码中,第二次进行while
check时,stream里待处理的内容是
[空格]test
这个开头处的空格,使程序落入了预先设计好的逻辑陷阱,于是程序什么也不干,直接给failbit置位,就跳出循环了。在循环外的时候显示为2。
用上述修改过的实验代码,可以比较清楚的显示出:该逻辑矛盾发生在第二次进行while
checking :
while checking 1 --> 1 2 4
0
test
while checking 2 -->
out of while loop before clearing
1 2 4
2
out of while loop after clearing
1 2 4
0
Press any key to continue . . .
不过仍然有个小疑惑,我根据您说的,也自己在vs2013里看了,既然eofbit ailbit badbit是static const,不能改变值,那么我们所说的置位,实际上并没有置位,只是返回了一个状态,比如到达文件尾,eofbit值并没有改变,不过返回了一个1,告诉我们到达文件尾了,然后我们人为的给它一个定义,说这叫eofbit置位了,来方便人理解? 是这个意思吗?
看来你还没理解透。
- 置位,置的是什么?是 std::ios_base::iostate
- @Mooophy 说
+
,实际有失偏颇。实际是|
(或) 的关系。如eofbit(1)
和failbit(2)
两个状态同时发生的时候,实际是:
0001
| 0010
----------
0011
即 std::ios_base::iostate
等于 3,这是你在 SO 上原问题最大的疑惑(看见你追问了。。。)
- Dietmar Kühl 大爷,是为了给你演示每一个状态出现的场景,才列出了各种情形。其他的你理解了,但
noskipws
好像还有疑问。@Mooophy 说的够清楚,我只补充一个示例代码:
char a, b, c;
std::istringstream iss (" 123");
iss >> std::skipws >> a >> b >> c;
std::cout << a << b << c << '\n';
iss.seekg(0);
iss >> std::noskipws >> a >> b >> c;
std::cout << a << b << c << '\n';
输出结果:
123
1
另外,@Mooophy 提到
需要指出的是,eofbit 、failbit等几个值的实现细节是machine dependent 的(我甚至怀疑这部分就不是standard规定范畴)。上述代码的运行环境是:windows 8.1 + vs2013。
我试了一下Ubuntu14.04+gcc4.82,运行结果是不一样的。
我彻底一点,帮你找出 C++ 标准(N3690) 上的定义:
其中 Table 124. 的内容如下:
可以看到,具体的值的确是没定义的。但注意顺序。
以下是 Clang 的底层实现:
typedef unsigned int iostate;
typedef iostate io_state;
static const iostate badbit = 0x1;
static const iostate eofbit = 0x2;
static const iostate failbit = 0x4;
static const iostate goodbit = 0x0;
可以看到,为啥说 Clang 是目前最棒的 C++ 编译器,连顺序都和标准上的一样。。。从低位到高位:
0001 // badbit
0010 // eofbit
0100 // failbit
0000 // goodbit
goodbit
的值,标准里定义了,必须是 0
。其他的没说。
对比一下 VS 的吧,它又淘气了。。。
@pezy 我理解您的意思了,之前知道改变了iostate的值,但是书上说“failbit被置位”这样类似的话语,实际上failbit的值没有改变啊?我们说的failbit置位 实际上是通过改变iostate的值来表现出“置位”这个概念。所以可以说按照您说的“置位std::ios_base::iostate”,我理解的意思对吧!
原来是用\
,长知识了 👍
关于noskipws的理解,根据你们二位的细心阐述,我觉得就是把空格当作一个字符来处理,不能忽略。
😄
恩,这次理解的没问题了。
但是书上说“failbit被置位”这样类似的话语
中文版扔掉好了。。。
SO 很强大,你用的也不错,看到 @Mooophy 给你点赞了,我也点了一个~
另,那老爷子多半不会再回复你了。。。基本可以结贴了。。。
@pezy 简直不能太赞!:+1: Clang编译器我第一次听说还是听您说的,不过目前对比来说最熟悉的编译器还是vs,有时间我试试clang,上次装linux时,准备试试gcc(windows下的gcc编译老是失败,估计我配置有问题),各种命令不会,实在耽误任务的完成,又换回windows了,有机会试试Clang。非常感谢!这个issue延伸知识的远超我的预计,赞赞赞 ! :smile:
@pezy 我在说那句话的时候,特意翻了英文版,英文版也说“the failbit is set ” ,英文不是太好,暂时扔不掉中文版,学习中文版可以更加迅速的帮我学习C++。Thx!我现在就去结贴!:smile:
英文版也说“the failbit is set ”
set
翻译为 置位,真令人费解呢!直译的话:failbit
被设置,即 设置为 failbit
,感觉就不会迷惑了。。。
@Ocxs 。。。我英语很次,明天等 @Mooophy 教你吧。或者看下他在知乎上曾经提到的一个答案:http://www.zhihu.com/question/27008201/answer/36651724 ,应该会有启发。
另外,我注意到你开了一个 repo 来记录读书笔记。我也有类似的习惯,并弄了个组织:https://github.com/ReadingLab
刚邀请了你加入来着,不知道你是否收到邮件了。期待你的参与~
@Mooophy 非常同意!🙆
⚠ issue 本身无 Git 机制,其实蛮不保险。我曾经有误点设置里某按钮而痛失全部 issue 的经历。
⚠ 整理问题与答案,看似费事,实际上是对知识的梳理。有人写blog的目的甚至就是这个。
你加个README之类的文件试试看,不要让repo完全空着。
我的想法是用新开的这个repo的issue进行中文讨论。
我擦,你俩真牛! 👍
@Ocxs 我强烈建议你,花点时间学习一下 Git 操作,最好习惯在本地操作与编辑文件。
下面有一些教程,非常简单:
- 交互教程[英语的]:http://pcottle.github.io/learnGitBranching/
- 跟着做教程:http://gitimmersion.googol.im/index.html
- 图解教程:http://marklodato.github.io/visual-git-guide/index-zh-cn.html
- 一本书:http://iissnan.com/progit/
希望您能找到自己喜欢的,熟悉 Git 的基本操作。对未来找工作,是有助力的。
@pezy 多谢,我会学的,但是你刚才教的那个重命名,我好像用的不行啊,我把ch1按照你说的,重命名为ch01,然后提交不了,我就在ch01里加了个readme,结果直接新建了ch01,ch1还在,所以刚才截图的那个edit之后如何操作呢?
@Ocxs 这就是为啥我紧接着给你推荐 Git 操作了,通过网页能做的事情太少了。譬如我说的 edit, 其实就是新建一个新文件夹,而删除,则根本无法通过网页操作(这个功能 Github 还未开发 https://github.com/isaacs/github/issues/225)。
所以,那些偷懒的办法也仅限于此,还是老老实实学 Git 命令吧。。。