C++ cin输入流 详细用法

我们来看下下面这段代码:

#include <iostream>
#include <vector>
#include <cstdlib>

int main()
{
  int num = 0;
  std::vector<int> ivec;
    do {
      std::cout << "please input some numbers:" << std::endl;
      while (std::cin >> num)//一直检测输入流状态知道遇到文件结束符(ctrl+z)或错误输入
    ivec.push_back(num);
      if (ivec.size() == 0)
    std::cout << "Error!" << std::endl;
    } while (ivec.size() == 0);
  system("pause");
  return 0;
}

现在我们基于这段代码进行一些讨论:

运行发现,当第一次输入正确的时候,则运行正确,如果第一次输入错误(例如以Ctrl+z然后按Enter),则程序会进入死循环:

分析以下原因:

首先需要了解cin的用法。C++输入缓冲机制规定当用户键入输入之后按下Enter键,便会将所有刚刚用户输入的一次性全送到缓冲区,而cin便会从输入缓冲区中读取数据。回车标志一次输入的完成,如果数据不够,则会等待用户继续输入;如果数据有多余,则将多余的数据存储在输入流缓冲区中,供下次使用。

举个例子:

while(std::cin >> num)//一直检测输入流状态知道遇到文件结束符(ctrl+z)或错误输入
  std::cout<< num << std::endl;

这条语句,如果输入一次1 2 3 ,执行结果是:

1

2

3

执行过程如下:

第一次运行std::cin >> num的时候,输入缓冲区是空的,当用户输入1 2 3 ctrl+z然后回车,这时候cin读入第一个整数1,然后输出1和换行,下一次执行 std::cin >> num的时候,缓冲区不为空,所以不再要求用户输入,直接读取第二个整数2,然后输出2和换行。以此类推,依次输出3 ,4。

然后cin检查到文件结束标志ctrl+z,cin>>num返回false,循环退出。

需要特别注意的一点是:当缓冲区中有残留数据时,cin会直接去读取缓冲区的数据而不会请求键盘输入。重要的是,回车符也会被存在输入缓冲区中。

有了这些知识,就可以解释前面代码中的现象了。如果第一次没有输入有效字符,以ctrl+z加上回车键结束输入后,回车符会被当成一个字符存入输入缓冲区。循环的时候,到了while(std::cin >> num)的时候,cin读取到文件结束符(Ctrl+z),cin的状态被置0,不再接收输入,所以ivec.size()等于0,程序再次进入下一个循环,由于 cin不再接受输入, `所以程序进入死循环。(之前看别人说是Enter的原因,但其实不是,在cin进行第二次读取之前,cin已经不再接受输入)

所以我们需要将代码做一点小小的修改:

int main()
{
  int num = 0;
  std::vector<int> ivec;
    do {
      std::cout << "please input some numbers:" << std::endl;
      while (std::cin >> num)//一直检测输入流状态知道遇到文件结束符(ctrl+z)或错误输入
    ivec.push_back(num);
    std::cin.clear();//添加这一行。
      if (ivec.size() == 0)
    std::cout << "Error!" << std::endl;
    } while (ivec.size() == 0);
  system("pause");
  return 0;
}

修改后的代码运行正常

代码中添加了std::cin.clear();,这个函数有两个作用:

i、用来更改cin的状态标示符的,cin在接收到错误的输入的时候,会设置状态位good。如果good位不为1,则cin不接受输入。如果下次输入前状态位没有改变那么即使清除了缓冲区数据流也无法输入。所以当输入流发生错误后,要想再次进行输入,必须添加std::cin.clear()。

ii、具有清除缓冲区的作用。

std::cin.ignore()这个函数不具备更改cin状态标识的功能,如果把上面代码中的std::cin.clear()函数换成std::cin.ignore,则程序在第一次输入不正确的情况下仍会进入死循环:

接着讨论第二个问题,把第二段代码修改以下:

int main()
{
  int num = 0, val1 = 0;
  std::vector<int> ivec;
    do {
      std::cout << "please input some numbers:" << std::endl;
      while (std::cin >> num)//一直检测输入流状态知道遇到文件结束符(ctrl+z)或错误输入
    ivec.push_back(num);
    std::cin.clear();
      if (ivec.size() == 0)
    std::cout << "Error!" << std::endl;
    } while (ivec.size() == 0);
    std::cin >> val1;
    std::cout << val1;
  system("pause");
  return 0;
}

运行后输入1 2 3 Ctrl+z 然后Enter,直接就这样了:

程序不会等待你输入,直接输出了val1的初始值0,这是为什么呢?

前面说过:当缓冲区中有残留数据时,cin会直接去读取缓冲区的数据而不会请求键盘输入。而且重要的是,回车符也会被存在输入缓冲区中。

程序运行后输入1 2 3 Ctrl+z然后Enter,while(std::cin >> num)一直检测输入流知道cin读取到文件结束符(Ctrl+z),循环结束,1、2、3都会被依次cin读取并从缓冲区中除去,留下一个Enter还在缓冲区中等待输入流,所以执行std::cin >> val1 这个语句的时候,cin会去缓冲区中读取数据,读取到Enter后输入结束,所以cout直接输出val1的初始值0。

所以我们需要将代码进行以下修改:

int main()
{
  int num = 0, val1 = 0;
  std::vector<int> ivec;
    do {
      std::cout << "please input some numbers:" << std::endl;
      while (std::cin >> num)//一直检测输入流状态知道遇到文件结束符(ctrl+z)或错误输入
    ivec.push_back(num);
    std::cin.clear();
      if (ivec.size() == 0)
    std::cout << "Error!" << std::endl;
    } while (ivec.size() == 0);
    std::cin.ignore();//添加这一句
    std::cin >> val1;
    std::cout << val1;
  system("pause");
  return 0;
}

在while循环之后添加std::cin.ignore(),在执行std::cin >> val1前,清除缓冲区中的内容,则程序运行正常。

但这里有产生一个问题,在用户输入的时候多次输入Enter,程序却不会出现输入错误,一直等待着用户输入,直到有正常数据输入:

这里值得说一下的是,当执行cin语句时,Enter可能会被当成是连续的空格符来处理,也可能被当成是结束符来处理,这个由cin的定义决定,而作为连续的空格则被忽略掉,

我们可以来测验最前面所说的一下造成程序进入死循环的原因,

在上面那段代码中std::cin.ignore后面再添加一句while( std::cin >> num );

如图所示,while(std::cin >> num)在接收到文件结束符后便不再接受输入,直接输出val1、val2的初始值0。如果说是里面残留了一个Enter的原因的话,那val1结束输入有可能,但两个同时结束输入就不太对了吧。

四个也一样:

加了std::cin.ignore()清除掉残留在缓冲区的Enter也不行:

所以一开始程序进入死循环的原因,是cin读取到文件结束符(Ctrl+z),cin的状态被置0,不再接收输入,所以ivec.size()一直等于0,进入死循环,就是这样。

以上内容其实可以总结成三点:

1、当cin接收到错误输入后,其状态会被改变,不再接收输入,只有用cin.clear()将cin的good状态恢复为 1之后,才能正常接收输入。

2、输入时键入的Enter也会被存储在缓冲区中。

3、Enter有时候会被读成连续的空格,有时候会被读成结束符,这个由cin自己判断决定。

时间: 2024-08-06 03:41:24

C++ cin输入流 详细用法的相关文章

C++学习45 流成员函数put输出单个字符 cin输入流详解 get()函数读入一个字符

在程序中一般用cout和插入运算符“<<”实现输出,cout流在内存中有相应的缓冲区.有时用户还有特殊的输出要求,例如只输出一个字符.ostream类除了提供上面介绍过的用于格式控制的成员函数外,还提供了专用于输出单个字符的成员函数put.如:    cout.put('a');调用该函数的结果是在屏幕上显示一个字符a.put函数的参数可以是字符或字符的ASCII代码(也可以是一个整型表达式).如    cout.put(65 + 32);也显示字符a,因为97是字符a的ASCII代码. 可以

超详细ofstream和ifstream详细用法

ofstream和ifstream详细用法 ofstream是从内存到硬盘,ifstream是从硬盘到内存,其实所谓的流缓冲就是内存空间(文章最末尾附上了MSDN中关于这两个函数的解释); 在C++中,有一个stream这个类,所有的I/O都以这个"流"类为基础的,包括我们要认识的文件I/O,stream这个类有两个重要的运算符: 1.插入器(<<) 向流输出数据.比如说系统有一个默认的标准输出流(cout),一般情况下就是指的显示器,所以,cout<<&quo

C++ ofstream和ifstream详细用法以及C语言的file用法

ofstream是从内存到硬盘,ifstream是从硬盘到内存,其实所谓的流缓冲就是内存空间; 在C++中,有一个stream这个类,所有的I/O都以这个"流"类为基础的,包括我们要认识的文件I/O,stream这个类有两个重要的运算符: 1.插入器(<<) 向流输出数据.比如说系统有一个默认的标准输出流(cout),一般情况下就是指的显示器,所以,cout<<"Write Stdout"<<'\n';就表示把字符串"W

Display:Block 详细用法

根据CSS规范的规定,每一个网页元素都有一个display属性,用于确定该元素的类型,每一个元素都有默认的display属性值,比如div元素,它的默认display属性值为"block",成为"块级"元素(block-level):而span元素的默认display属性值为"inline",称为"行内"元素. 块级元素: 动占据一定矩形空间,可以通过设置高度.宽度.内外边距等属性,来调整的这个矩形的样子: 行内元素: 自己的

DOM Style样式对象的详细用法

DOM Style样式对象的详细用法 HTML Style样式比较复杂,相应访问.修改方法也有所差异.参考相关资料,整理如下. 典型Html文件如下,有三种定义方式. <head>     <style type="text/css">                /* 内部样式 */       h3 {color:green;}     </style>             <!-- 外部样式 style.css -->    

文件/目录权限设置命令chmod的详细用法

chmod是文件/目录权限设置的命令,在Linux中经常遇到,本博文以下总结chmod的详细用法. Linux/Unix的档案调用权限分为三级,即档案拥有者user.群组group.其他other.u表示该档案的拥有者,g表示与该档案的拥有者属于同一个群体(group)者,o表示其他以外的人,a表示这三者皆是. + 表示增加权限.- 表示取消权限.= 表示唯一设定权限. r表示可读取,w表示可写入,x表示可执行. 举例说明: (1).将档案file1.txt 设为所有人皆可读取: chmod u

mysql中游标在存储过程中的详细用法

昨天写的一个东东,分享下给大家. drop PROCEDURE  if exists sp_cleanUserData; CREATE  PROCEDURE `sp_cleanUserData`() BEGIN /*定义游标*/ declare v_dt bigint(20) default 0 ; declare v_num INT DEFAULT 0; /*游标循环到末尾时给定义的常量赋值*/ declare cur_userId   CURSOR FOR select  userId fr

C++中cin.clear()的用法

我们谈谈cin.clear的作用,第一次看到这东西,很多人以为就是清空cin里面的数据流,而实际上却与此相差很远,首先我们看看以下代码: #include <iostream>  using namespace std;  int main()   {              int a;              cin>>a;              cout<<cin.rdstate()<<endl;              if(cin.rds

Linux中find、grep命令详细用法

在linux下面工作,有些命令能够大大提高效率.本文就向大家介绍find.grep命令,他哥俩可以算是必会的linux命令,我几乎每天都要用到他们.本文结构如下: find命令 find命令的一般形式 find命令的常用选项及实例 find与xargs grep命令 grep命令的一般形式 grep正则表达式元字符集(基本集) grep命令的常用选项及实例 1.find命令 find命令是一个无处不在命令,是linux中最有用的命令之一.find命令用于:在一个目录(及子目录)中搜索文件,你可以