注册 登录
编程论坛 C++教室

输入流的困惑

yhtian619 发布于 2010-02-07 22:11, 1430 次点击
程序如下:
#include <iostream>
using namespace std ;

int main()
{
    int ival ;
    while(cin >> ival, !cin.eof())
    {
        if(cin.bad())
            throw runtime_error(" IO stream corrupted ") ;
        if(cin.fail())
        {
            cerr << " bad data, try again " ;
            cin.clear(istream::failbit);
            continue ;
        }
    }
    return 0 ;
}
输入如果是int型数据程序当然没有问题了,但如果输入其他的,比如char型,程序就会无限循环了,不断输出bad data,try again ,输入流并没有等待我输入数据。各位大虾,请问是怎么回事啊?是输入缓冲区的问题嘛?
12 回复
#2
debroa7232010-02-08 19:48
while(cin >> ival)
你为什么不这样呢?说说你的想法!!
#3
_DaNciNg_2010-02-08 19:59
想知道while循环中可以用逗号吗
表示的是什么意思
小弟惭愧,没有见过。
#4
pangding2010-02-08 22:38
由于读一个 int 失败,所以你输的那个 char 根本没有读进来。所以下回再读还是 fail。
#5
ly8610142010-02-09 15:08
回复 4楼 pangding
cin.clear(istream::failbit);之后,流又恢复有效状态了,下回读之前应该不是fail吧?
我感觉这段代码是有些问题,按理说不应该是无限循环。
#6
pangding2010-02-09 17:16
我说的可能不清楚。
不是说 fail 没清回来。而是你清了之后转回来读的是什么?还是阻塞在那里的那个 char ,当发生读错误的时候,cin 是不可能把输入流清空的,没读的还是在那里。但你还是想当 int 读,能读吗?还得 fail。
#7
ly8610142010-02-09 20:22
回复 6楼 pangding
不大明白为什么读入的时候,cin不能清空流啊?为什么用清空这个词呢?
#8
yhtian6192010-02-09 20:43
回复 楼主 yhtian619
首先,while循环里是个逗号表达式,其真假依赖于!cin.eof()的真假。如果直接用while( cin >> val ),且读入char型数据的话,输入流马上失效,循环就会结束,根本没法保证val有初值。第二,!cin.eof()判断是否到了文件结尾,跟流的有效和无效应该没有什么关系的吧,但什么时候代表文件结尾我暂时还不清楚。
#9
yhtian6192010-02-09 20:49
回复 4楼 pangding
也就是说如果输入缓冲区还有数据没读走,就算把输入流恢复为有效,还是会先读之前的数据,所以才会不等待用户输入数据就直接跳过了?那有没有办法在下次读入之前,把缓冲区清空啊?
#10
yhtian6192010-02-09 20:50
回复 3楼 _DaNciNg_
里面是个逗号表达式,可以求值!
#11
yhtian6192010-02-09 20:51
程序的意图是读入数据给val,并保证在处理val之前,它有一个确切的初始值,处理部分我没给出。
#12
pangding2010-02-11 01:07
回复 7楼 ly861014
比如你想读一系列 int 但,由于失误或是什么其它原因输入的是这样的:
1 2 3 4 a 5 6 7
你是不是想跳过那个 a 然后把 1 - 7 都读进来?
但你读完4后,输入缓冲区就变成:
a 5 6 7
由于这次读入 int 失败,所以 cin 的 fail 位设置。不过 a 也不是 int 所以也没读进来。
你虽然清了 fail 标志,但那个 a 还再,所以现在缓冲区还是:
a 5 6 7
你的循环又回去读 int ,但还是 a 所以就开始死循环了。


一般的处理方法是当出现读错误的时候,转用读 char 的方式,分析用户输入的是什么。如果是合理的其它输入,就使程序转入合适的分支继续执行。
如果确实是错误输入,可选策略有几种,可以预先和用户约定。
比如就认为输入到此,截断后面的输入。然后可以在标准错误上提示,然后把剩余字串列印在屏幕上。
还可以把这个没能读出的部分移到输入的最后,然后尝试继续读后面的部分。最后把所有没能读出的就完在最末尾了,这时再输出错误提示,和列印所有没分析成功的部分。
这两种做法都比较常见,后者明显复杂一些,但并不意味着好。

比如输入链表,可以约定用 end 这个指令结束一个链表的输入。那么输入两个链表可像下面这样:
1 2 3 4 5 end 6 7 8 9 10 end
这样就可以建立两个链表。但可能会变成这样:
1 2 3 4 5 and 6 7 8 9 10 end

如果 and 是令一个指令,那那个用户可能要遭遇一些麻烦,因为如果这种输入没有其它语法错误的话,可能程序会认为用户正想做 and 要求的事。出错的责任应该由用户负责(当然好的程序可能得提供 徹销 之类的方法)。

这种情况其实经常遇到,我举个例子,比如你在窗口模式下想选种一串连续的文件,可能会先点中一个文件然后按 shift 再按 方向键上面的那个 end 键。但手误(有的键盘的布局不一样,这挺令人讨厌)可能会按到 delete 键上。这意味着彻底删除文件!!你一定不想这样,而且这么做可能还没有徹销的机会!!想操作系统是怎么做的?弹出一个对话框让你确认一下。end 键不会导致这个结果,这个意外的提示会使用户清楚的意识到自己刚刚可能做了一件并非自己期望的事!!
不过这种做法可能也不是什么时候都妥当,当你确实就想删东西的时候,可能会厌倦什么时候都问,很烦!!有时几种提示夹杂的时候,一些本来就能期望的结果,可能导致疲劳。用户会“闭着眼”一通乱点,这可能会导致一些有价值的提示没能看清就点没了。究竟应该怎么折衷?这个问题其实有一定的讨论价值~ 跑题跑的有点远了~~


如果没有 and 这个指令,那就好了,立刻告诉它有问题,弹出提示,用户一看就明白了。再打一次打对就是了。
这时如果你用第二种方法,就有点麻烦了,因为本来想加在第二个表里的东西跑到了第一个里面。用户还得想办法把它们移动(从第一个中删除,并插入)到第二个里去。
如果这几个数据就在最后还好,不过有些结构会在数据入列的时候排序,那可能这几个值就找不着了!!用户会很苦恼。

不过什么都问用户,用户也会烦。折衷的方法就是约定一些出错的默认处理方法,但应该考虑这些方法可以定制,并允许用户覆盖。想想是不是一般你用的软件都可以设置首选项?
好多东西其实都挺有讲究,不是那么容易。要记住的就是最不好伺候的就是用户,他们既爱犯错又爱抱怨。所以一般情况下尽量把死的算法部分与用户可能接触的交互部分分开。
#13
yikuaidao2010-02-12 13:30
要记住的就是最不好伺候的就是用户,他们既爱犯错又爱抱怨。所以一般情况下尽量把死的算法部分与用户可能接触的交互部分分开。

谢谢!
1