C++ primer plus 第17 章 输入、输出和文件:使用 cin 进行输入

C++ primer plus 第17 章 输入、输出和文件:使用 cin 进行输入

C++ primer plus 第17 章 输入、输出和文件:使用 cin 进行输入


17.3 使用 cin 进行输入

现在来介绍输入,即如何给程序提供数据。cin对象将标准输入表示为字节流。通常情况下,通过键盘来生成这种字符流。如果键入字符序列2011,cin对象将从输入流中抽取这几个字符。输入可以是字符串的一部分、imnt 值、foat 值,也可以是其他类型。因此,抽取还涉及了类型转换。cin 对象根据接收值的变量的类型,使用其方法将字符序列转换为所需的类型。
通常,可以这样使用cin:

cin >> value holder;

其中,value holder 为存储输入的内存单元,它可以是变量、引用、被解除引用的指针,也可以是类或结构的成员。cin解释输入的方式取决于 valuc holder的数据类型。istream类(在 iostream 头文件中定义)重载了抽取运算符>>,使之能够识别下面这些基本类型:

  • signed char &:
  • unsigned char & :
  • char &;short &;
  • unsigned short &:
  • int &;
  • unsigned int &:
  • long &;
  • unsigned long &:long long&(C++11):
  • unsigned long long&(C++11);
  • float &;
  • double &;
  • long double &.
    这些运算符函数被称为格式化输入函数(formatted input functions),因为它们可以将输入数据转换为目标指定的格式。
    典型的运算符函数的原型如下:
istream & operator>>(int &);

参数和返回值都是引用。引用参数(参见第8章)意味着下面这样的语句将导致operator>>()函数处理变量stasize本身,而不是像常规参数那样处理它的副本:cin >> staff size;
由于参数类型为引用,因此cin能够直接修改用作参数的变量的值。例如,上述语句将直接修改变量stan_size 的值。稍后将介绍引用返回值的重要意义。首先来看抽取运算符的类型转换方面。对于上述列出的名种类型的参数,抽取运算符将字符输入转换为指定类型的值。例如,假设safsize的类型为int,则编译器将:

cin >> staff size;

与下面的原型匹配:

istream & operator>>(int &);

对应于上述原型的函数将读取发送给程序的字符流(假设为字符2、3、1、8和4)。对于使用2字节im 的系统来说,函数将把这些字符转换为整数23184的2字节二进制表示。如果stafsize的类型为 double,则
cin 将使用 opcrator>>(double&)将上述输入转换为值 23184.0的8字节浮点表示。顺便说一句,可以将 hex、oct和 dec 控制符与 cin 一起使用,来指定将整数输入解释为十六进制、八进制还是十进制格式。例如,下面的语句将输入12或 0x12 解释为十六进制的 12 或十进制的 18,而将 或FF解释为十进制的255:

cin >> hex;

istream 类还为下列字符指针类型重载了>>抽取运算符:

signed char**:
char*:
unsigned char *

对于这种类型的参数,抽取运算符将读取输入中的下一个单词,将它放置到指定的地址,并加上一个空值字符,使之成为一个字符串。例如,假设有这样一段代码:

cout << "Enter your first name:\n";
char name[20];
cin >> name;

如果通过键入Liz 来进行响应,则抽取运算符将把字符Liz0放到name 数组中(\0 表示末尾的空值字符)。name 标识符是一个char数组名,可作为数组第一个元素的地址,这使name的类型为char*(指向char的指针)。
每个抽取运算符都返回调用对象的引用,这使得能够将输入拼接起来,就像拼接输出那样:

char name[20];
float fee;
int group;
cin >>name >>fee >> group;

其中,cin>>name返回的cin对象成了处理fe的对象,

17.3.1 cin>>如何检查输入

不同版本的抽取运算符査看输入流的方法是相同的。它们跳过空白(空格、换行符和制表符),直到遇到非空白字符。即使对于单字符模式(参数类型为char、unsigncdchar或signed char),情况也是如此,但对于C语言的字符输入函数,情况并非如此(参见图17.5)。在单字符模式下,>>运算符将读取该字符,将它放置到指定的位置。在其他模式下,>>运算符将读取一个指定类型的数据。也就是说,它读取从非空白字符开始,到与目标类型不匹配的第一个字符之间的全部内容。
例如,对于下面的代码:

int elevation;
cin >> elevation;

假设键入下面的字符:
-1232运算符将读取字符-、1、2和3,因为它们都是整数的有效部分。但Z字符不是有效字符,因此输入中最后一个可接受的字符是3。Z将留在输入流中,下一个cin 语句将从这里开始读取。与此同时,运算符将字符序列-123转换为一个整数值,并将它赋给elevation。
输入有时可能没有满足程序的期望。例如,假设输入的是 Zcar,而不是–123Z。在这种情况下,抽取运算符将不会修改 elevation 的值,并返回 0(如果 istream 对象的错误状态被设置,if或 while 语句将判定该对象为 &lse这将在木章后面做更详细的介绍)。返回值 lse 让程序能够检査输入是否满足要求,如程序清单 17.11 所示。

程序清单 17.11 check_it.Cpp

// check_it.cpp -- checking for valid input
#include <iostream>

int main()
{
   
   
    using namespace std;
    cout << "Enter numbers: ";

    int sum = 0;
    int input;
    while (cin >> input)
    {
   
   
        sum += input;
    }

    cout << "Last value entered = " << input << endl;
    cout << "Sum = " << sum << endl;
/* keeping output window open */
/*
    cin.clear();
    while (cin.get() != '\n')
        continue;
    cin.get();
*/
    return 0;
}

由于输入被缓冲。因此通过键盘输入的第二行在用户按下回车键之前,不会被发送给程序。然而,循环在字符乙处停止了对输入的处理,因此它不与任何一种浮点格式匹配。输入与预期格式不匹配反过来将导致表达式 cin>>input 的结果为false,因此 while 循环被终止。

17.3.2 流状态

我们来进一步看看不适当的输入会造成什么后果。cin或cout 对象包含一个描述流状态(stream state)的数据成员(从ios base 类那里继承的)。流状态(被定义为iostate 类型,而iostate 是一种 bitmask 类型)由3个ios base元素组成:cofbit、badbit或failbit,其中每个元素都是一位,可以是1(设置)或0(清除)。当 cin 操作到达文件末尾时,它将设置eofbit:当cin 操作未能读取到预期的字符时(像前一个例子那样),它将设置ailbit。I0失败(如试图读取不可访问的文件或试图写入写保护的磁盘),也可能将failbit 设置为1。在一些无法诊断的失败破坏流时,badbit元素将被设置(实现没有必要就哪些情况下设置 faibit,哪些情况下设置 badbit达成一致)。当全部3个状态位都设置为0时,说明一切顺利。程序可以检查流状态,并使用这种信息来决定下一步做什么。表17.4列出了这些位和一些报告或改变流状态的ios base 方法。
在这里插入图片描述
设置状态
表17.4中的两种方法–clear()和setstate()很相似。它们都重置状态,但采取的方式不同。clear()方法将状态设置为它的参数。因此,下面的调用将使用默认参数0,这将清除全部3个状态位(eofbit、badbi和 failbit):

clear();

同样,下面的调用将状态设置为cofbit;也就是说,cofbit将被设置,另外两个状态位被清除

clear(eofbit);

而setstate()方法只影响其参数中已设置的位。因此,下面的调用将设置cofbit,而不会影响其他位:

setstate(eofbit):

因此,如果failbit被设置,则仍将被设置。
为什么需要重新设置流状态呢?对于程序员来说,最常见的理由是,在输入不匹配或到达文件尾时需要使用不带参数的 cear()重新打开输入。这样做是否有意义,取决于程序要执行的任务。稍后将介绍一些例子。setstate()的主要用途是为输入和输出函数提供一种修改状态的途径。例如,如果num是一个 int,则下面的调用将可能导致 operator >>(int&)使用 setstate( )设置 failbit 或 eofbit:

cin >>num:/read an int

2.I/O 和异常

假设某个输入函数设置了eofbit,这是否会导致异常被引发呢?在默认情况下,答案是否定的。但可
以使用 exceptions()方法来控制异常如何被处理。首先,介绍一些背景知识。exceptions()方法返回一个位字段,它包含3位,分别对应于eotbit、failbit和 badbit。修改流状态涉及clear()或setstate(),这都将使用clear()。修改流状态后,clear()方法将当前的流状态与 exceptions()返回的值进行比较。如果在返回值中某一位被设置,而当前状态中的对应位也被设置则 clear()将引发 ios base::àilure 异常。如果两个值都设置了 badbit,将发生这种情况。如果 exceptions()返回goodbit,则不会引发任何异常。ios base::ailure 异常类是从 std::exception 类派生而来的,因此包含一个 whal()
方法。exceptions()的默认设置为goodbit,也就是说,没有引发异常。但重载的exceptions(iostate)函数使得能够控制其行为:

cin.exceptions(badbit);// setting badbit causes exception to be thrown

位运算符 OR(在附录E讨论)使得能够指定多位。例如,如果badbit或eobit 随后被设置,下面的语句将引发异常:

cin.exceptions(badbiteofbit):

程序清单17.12对程序清单17.11进行了修改,以便程序能够在failbit 被设置时引发并捕获异常。

程序清单 17.12 cinexcp.cpp

// cinexcp.cpp -- having cin throw an exception
#include <iostream>
#include <exception>

int main()
{
   
   
    using namespace std;
    // have failbit cause an exception to be thrown
    cin.exceptions(ios_base::failbit);
    cout << 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值