本节书摘来自华章出版社《C++程序设计教程(第3版)》一书中的第3章,第3.3节cin输入流,作者张志航,更多章节内容可以访问云栖社区“华章计算机”公众号查看
3.3 cin输入流
与cout输出流对应的是cin输入流,使用cin输入流可以方便地从键盘输入数据至内存中正在运行的程序。
在程序执行期间,使用cin来给变量输入数据,其一般格式为:
cin >> <变量名1> [ >> <变量名2> >> … >> <变量名n> ]
其中,运算符“>>”用来从操作系统的输入缓冲区中提取字符,然后送到程序的内存变量中。在C++中这种输入操作称为“提取”(extracting)或“得到”(getting),因此运算符“>>”通常称为提取运算符,表示将暂停程序的执行,等待用户从键盘上输入相应的数据。
输入流的使用方法记忆起来非常简单,可以将cin想象成键盘,提取运算符“>>”想象成指向箭头。由于“箭头”是从cin“指向”内存变量的,因此代表了从键盘获得数据,传入到内存变量中。
与插入运算符“<<”类似,在C++中,运算符“>>”具有多种功能,它除了可以做输入流的提取运算符之外,还可以作为位运算的右移运算符来使用。但是,当它和cin标识符连用时,它只能作为提取运算符来使用。这同样是利用了面向对象编程技术中的运算符重载技术,具体的细节将在后面的有关运算符重载部分加以详细说明。
根据上面的格式说明,在每个提取运算符后面只能跟一个变量名,但“>> <变量名>”的组合可以重复多次。即在cin后面使用一次“>> <变量名>”可以给一个变量输入数据,在cin后边重复使用多次“>> <变量名>”可以一次给多个变量输入数据。例如,假设有变量声明:
`int a, b;
float c, d;
char e, f;`
在程序执行期间,要求把从键盘上输入的数据送给以上6个变量时,可以用cin按照如下方式来实现:
`cin >> a >> b; // A
cin >> c >> d; // B
cin >> e >> f; // C`
当执行到A行语句时,因为有数据流提取运算符“>>”存在,程序停止运行,等待用户从键盘上输入数据。假设这时用户输入:
100 200
则输入流操作符会根据顺序和变量的类型将100赋给整型变量a,将200赋给整型变量b。其中,表示回车键。在使用cin接收数值数据时(包括整型数据和实型数据),为了区分排列在一起的若干个数据,需要在相邻的数据之间加入分隔符。这里的“分隔符”在C++中具有特定的含义,主要包含3种:(空格键)、(Tab键)和< Enter>(回车键)。一般在输入的数值数据之间用一个或多个分隔符隔开,系统在进行处理时,会将相邻的多个分隔符视为一个来进行处理。
由于3种分隔符在作用上是等价的,因此用户可以任选一种分隔符来输入数值数据。输入的方式也就跟着变化了,上面用户的输入方式也可以改为如下方式:
100
200
或者
100 200
当前面的程序执行到B行时,又开始了一个新的输入流操作,又重新开始等待用户从键盘上输入数据。只不过这时提取操作符后面的变量类型变成了实型数据类型。假设用户输入:
3.141593 77
则将3.141 593赋给了实型变量c,将77.0赋给了实型变量d(数据类型以变量的类型为准)。提取数据的方法和原理与A行完全相同。
在数值数据被分隔符区分以后,如果要使得计算机接收数据,在每行数据输入完毕后还要输入回车键加以确认。在每行末尾输入回车键的作用是:
1)告诉cin一批数据已经输入完毕,cin开始提取用户输入的数据(忽略分隔符),并依次将所提取的数据赋值给cin中所列举的变量中尚未获得数据的变量。
2)在屏幕上显示光标换行,为下一行的输入或者显示程序的输出结果做好准备,起到输入数据之间以及输入数据与输出结果之间的分隔符的作用。
当cin遇到回车键时,如果用户输入的数据与输入语句中等待获得数值的变量的个数不等,会出现两种情况的处理:
1)用户输入的数据的个数小于变量的个数。则此时在提取完输入的有效数据后仍有变量没有获得数值,当前的输入语句不会结束,会继续等待用户输入新的一批数据。当用户继续输入数据并且按回车键确认输入后,cin输入语句会继续提取新的输入行中的数值给尚未获得数值的变量,直到当前输入语句中的所有变量都获得了具体的数值为止。在上面的例子中,直到用户输入了两个数值100和200,A行的a变量和b变量分别获得数值后,A行才执行结束。否则程序将会一直等待用户继续输入数值。
2)用户输入的数据个数大于变量的个数。则输入语句只依次提取输入行中的部分数值给变量。而多余的数值会被下一条cin输入语句中的变量所提取,或者在没有后续的输入语句的情况下被程序舍弃。
假设在执行A行时,用户输入:
100 200 3.141593 77
A行的cin会将输入行中的100和200分别赋给变量a和b,这时A行中的两个整型变量都获得了有效的数据,A行执行完毕;接着执行B行,因为用户输入行中的数据没有被A行提取完,于是B行接着提取,把用户输入行的后两个数据3.141 593和77.0分别赋给实型变量c和d。根据上面的例子可以总结出:在输入数值数据时,只要按照分隔符和行末回车符的规则来输入,可以将4个输入数据在一行内输入、在两行内输入,或者分多行输入。
如果用户输入的一行仅仅是一个回车键,则cin把该键作为分隔符来处理(将其忽略),而不是行末回车符。这时没有数据输送给变量,只是在屏幕上显示光标换行。程序会继续等待用户输入数据,直到用户输入有效数据为止。
在cin输入流的使用过程中,程序员不需要指定输入的数据类型,输入数据的类型由cin根据变量的类型来自动判定。因此,从键盘上输入数据的个数、类型及顺序必须与cin中列举的变量一一对应。若输入的类型不对,则输入的数据不正确。例如:
`int a, b;
cin >> a >> b;`
执行cin时,若输入字符数据:
D F
则由于用户输入的数据是字符类型,而对应的接收变量是整数类型,两者类型不匹配,变量a得不到有效值,其值为0。而由于前面已经出现错误,cin语句停止执行,则后续的变量b无法继续获得数值,其值为一个随机数。同时,错误会继续向后延伸,造成后续的cin语句也不能正确提取数据。
再如:
`int a;
float b;
cin >> b >> a;
执行cin时,若输入:
300 1.234 `
则根据第2章学习的内容,实型b变量进行数据类型转换,将整数300转换为实数300.0,最终获得300.0的数值,影响不大。而整型变量a也同样进行数据类型转换,将小数1.234转换为整数1,小数部分被丢弃。这样可能会造成后续的、有变量a参与计算的计算结果产生极大偏差。
上面我们以数值数据的输入为例,介绍了cin输入流的基本使用方法。在输入整型数据时,除了标准的输入方法以外,还有一些其他格式的输入方法,并且字符数据输入的写法也与数值数据输入的写法不同,下面我们就这些不同点分别加以具体的说明。
3.3.1 输入十六进制或者八进制数据
与前面提到的cout中的处理类似,对于整型变量,从键盘上输入的数据默认为十进制。除此以外,还可以按照八进制或者十六进制来输入数据。如前面例子中所示,在缺省的情况下,系统规定输入的整数都是十进制数据。当要求按照八进制或者十六进制输入数据时,必须在cin中指明相应的数据类型:hex表示十六进制,oct表示八进制,dec表示十进制。具体应用如例3.6所示。
例3.6 演示不同进制的输入输出流使用方法。
`include
using namespace std;
int main()
{
int a,b,c,d; // 变量名a,b,c,d
cin >> hex >> a; //指明输入为十六进制数
cin >> oct >> b; //指明输入为八进制数
cin >> c; //输入仍旧是八进制数
cin >> dec >> d; //指明输入为十进制数
cout << a <<','<< b <<','<< c <<','<< d << endl;
return 0;
}`
当执行到语句cin时,若输入的数据为:
20 21 22 23
则根据程序的进制设定,将第一个数20作为十六进制数赋值给变量a,将第二个数21和第三个数22作为八进制数分别赋值给变量b和c,将第四个数23作为十进制数赋值给变量d。无论将整数按照什么进制输入变量,在变量中都是按照二进制数的方式存放的。在输出的时候如果不加特别说明,系统会默认按照十进制的方式进行输出。在例3.6中针对上述输入的输出结果为:
32, 17, 18, 23
其中,十进制的32对应的是十六进制的20;十进制的17和18对应的是八进制的21和22;最后一个变量原来就是按照十进制形式输入的,因此输出依然是23,没有变化。
在C++中,整数常量和变量都可以表现为十六进制和八进制的形式。无论按照哪种进制表现的整数其本质都是以二进制补码形式存储的,都可以混合运算。在某些特殊的运算中(如位运算等),直接使用十六进制或八进制数据进行运算可以得到更加清晰的结果。要做到直接使用十六进制或八进制数据进行输入,必须在cin中指明输入数据时所用的数制。
使用非十进制输入数据时,要注意以下几点。
1)八进制或十六进制数的输入只能适用于整型变量,不适用于字符型变量、实型变量。
2)当在cin中指明使用的输入数制后,则所指明的数制一直有效,直到在下一个cin语句中指明采用不同的输入数制时为止。例如在例3.6中,输入变量c的值时,虽然没有再次指定按照八进制输入,但程序仍然会按照八进制进行输入。
3)用户从键盘输入数据的格式、个数和类型必须与cin中所列举的变量类型一一对应。一旦输入出错,不仅使当前的输入数据不正确,而且使得后面的提取数据也不正确。具体的例子请参考上面对输入流基本操作的介绍。
使用输入流输入数据时,如果出现输入错误,可以通过相关函数来检测错误,并且可以根据具体的错误进行相应的处理,具体操作将在第14章进行详细的介绍。
3.3.2 输入字符数据
当要为字符变量输入数据时,输入的数据必须是字符型数据。此时cin的格式及用法与输入十进制整数和实数时是相同的。设有如下程序片段:
`char c1, c2, c3, c4;
cin >> c1 >> c2 >> c3;`
当执行cin语句时,cin等待用户从键盘上输入数据。如果输入:
A b C
则cin分别将字符A、b、C赋值给字符型变量c1、c2、c3,如果输入:
AbC
cin也同样分别将字符A、b、C赋值给字符型变量c1、c2、c3,即字符之间是否有分隔符都一样。在缺省的情况下,cin会自动过滤输入的空格等分隔符(参考输入流基本功能介绍中的分隔符概念),输入流cin不会将输入的空格和回车键等分隔符赋给字符型变量。
在使用输入流输入字符数据时,最容易出现的错误是将数字与字符数据混淆,如例3.7所示。
例3.7 演示输入输出流的字符输入方法。
`include
using namespace std;
int main()
{
int a, b;
char c, d;
cin >> a >> b;
cin >> c >> d;
cout << a <<','<< b <<','<< c <<','<< d << endl;
return 0;
}`
假设用户输入如下数据:
12 34 5678
则由于变量a和变量b是整数类型,输入流cin会根据分隔符的标识,将12赋值给变量a,将34赋值给变量b。接下来就是给字符类型变量c和变量d赋值了。根据第2章学习的知识,我们知道一个字符占用一字节的空间,一个字符变量存放一个字符。因此,虽然5678是连写在一起的数字,但是输入流不会将它们当成一个整体赋值给字符变量,而是将它们看成4个连续的字符。'5'字符将被赋值给变量c,'6'字符将被赋值给变量d,而剩余的'7'字符和'8'字符由于没有对应的变量接收,将被舍弃。
在进行字符输入时,不但可以按照顺序输入字符,还可以对输入的内容有选择地接收,这时就要用到函数cin.ignore()了。cin.ignore()的作用是在字符读取过程中忽略若干个字符,而读取后面的字符。其格式为:
cin.ignore( <忽略的字符个数> );
例如将例3.7中程序的输入修改如下:
`cin >> a >> b;
cin.ignore(3);
cin >> c >> d;`
同样输入如下数据:
12 34 5678
但这时的输出结果却变化如下:
12, 34, 7, 8
可以看出,cin.ignore(3)语句让程序忽略了整数34后面的3个字符(包括空格符),而直接从第四个字符读起。将'7'字符赋值给了变量c,将'8'字符赋值给了变量d。
前面提到,输入流cin不会将输入的空格和回车键等分隔符赋给字符型变量。由于分隔符也是有效字符,那么当用户想要将3种分隔符作为字符输入计算机时,如何实现呢?借助函数cin.get()可以实现上述的要求。函数cin.get()的作用是把从键盘上输入的每一个字符,包括空格符和回车符等分隔符都作为一个输入字符赋给字符型变量。其格式为:
cin.get( <字符型变量> );
函数cin.get()从输入行中取出一个字符,并将它赋值给对应的字符型变量。该语句一次只能从输入行中提取一个字符。例如:
`char c5, c6, c7, c8;
cin.get(c5);
cin.get(c6);
cin.get(c7);
cin.get(c8);`
当程序执行第一行输入语句时,如果用户输入:
A B
在输入字符A前没有空格,在字符A与B之间有一个空格,则根据前面的介绍,程序会将字符A、空格、字符B分别赋值给变量c5、c6、c7;第四行语句中的c8变量也将获得字符值,即回车字符。也就是对于cin.get()来说,用户一共输入了4个字符。若第四行使用语句:
cin >> c8;
代替cin.get(c8),则cin将回车作为确认符和分隔符处理,在屏幕上显示光标换行,但并不会将回车符存入变量c8。这时由于变量c8尚未获得有效值,程序会一直等待用户继续输入一个有效字符。