《C++ Primer Plus》中的cin与cout
使用cout进行C++输出
cout << "Come up and C++ me some time.";
<<符号表示该语句把字符串发送给cout,该符号指出了信息流动的路径。cout是一个预定义的对象,知道如何显示字符串、数字和单个字符等。
输出是一个流,即从程序流出的一系列字符。cout对象表示这种流,其属性是在iostream文件中定义的。cout对象属性包括一个插入运算符,它可以将其右侧的信息插入到流中。
cout的新花样
cout的智能行为源自C++的面向对象特征。实际上,C++插入运算符(<<)将根据其后的数据类型相应的调整其行为,这是一个运算符重载的例子。
使用cin
cin >> carrots;
从这条语句可知,信息从cin流向carrots。就像C++将输出看作是流出程序的字符流一样,它也将输入看作是流入程序的字符流。iostream文件将cin定义为一个表示这种流的对象。输出时,<<运算符将字符穿插入到输入流中,输入时,cin使用>>运算符从输入流中抽取字符。通常,需要在运算符右侧提供一个变量,以接受抽取的信息(符号<<和>>被选择用来指示信息流的方向)。
使用cout进行拼接
iostream文件定义了<<运算符,以便可以像下面这样合并(拼接)输出:
cout << "Now you have " << carrots << " carrots." << endl;
整数字面值
在默认情况下,cout以十进制格式显示整数,而不管这些整在程序中是如何书写的。如果要以十六进制或八进制方式显示值,则可以使用cout的一些特殊特性。头文件iostream提供了控制符dec、hex和oct,分别用于指示cout以十进制,十六进制和八进制格式显示整数。默认格式为十进制,在修改格式之前,原来的格式将一直有效。
成员函数cout.put()
类ostream有一个put成员函数,用来输出字符。只能通过特定的对象(例如这里的cout对象)来使用成员函数。要通过对象(如cout)使用成员函数,必须用句点将对象名和函数名称(put())连接起来。句点被称为成员运算符。cout.put()的意思是,通过对象cout来使用函数put()。cout.put()成员函数提供了一种显示字符的方法,可以代替<<运算符。
字符串输入
cin使用空白(空格、制表符和换行符)来确定字符串的结束位置,这意味着cin在获取字符数组输入时只读取一个单词。读取该单词后,cin将该字符串放到数组中,并自动在结尾添加空字符。
每次读取一行字符串输入
istream中的类(如cin)提供了一些面向行的类成员函数:getline()和get()。这两个函数都读取一行输入,直到到达换行符。然而,随后getline()将丢弃换行符,而get()将换行符保留在输入序列中。
getline()函数读取整行,它使用通过回车键入的换行符来确定输入结尾。要调用这种方法,可以使用cin.getline()。该函数有两个参数,第一个参数是用来存储输入行的数组的名称,第二个参数是要读取的字符数。如果这个参数为20,则函数最多读取19个字符,余下的空间用于存储自动在结尾处添加的空字符。getline成员函数在读取指定数目的字符或遇到换行符时停止读取。
imstream类有另一个名为get()的成员函数,该函数有几种变体。其中一种变体的工作方式与getline()类似,他们接受的参数相同,解释参数的方式也相同,并且都读取到行尾。但get并不再读取并丢弃换行符,而是将其留在输入队列中。假设连续两次调用get():
cin.get(name, ArSize); cin.get(dessert, ArSize);
由于第一次调用后,换行符将留在输入队列中,因此第二次调用时看到的第一个字符便是换行符。因此get()认为已到达行尾,而没有发现任何可读取的内容。
使用不带任何参数的cin.get()调用可读取下一个字符(即使是换行符),因此可以用它来处理换行符,为读取下一行输入做好准备。也就是说,可以采用下面的调用序列:
cin.get(name, ArSize); cin.get(); cin.get(dessert, ArSize);
另一种使用get()的方式是将两个类成员函数拼接起来(合并),如下所示:
cin.get(name, ArSize).get();
之所以可以这样做,是由于cin.get(name, ArSize)返回一个cin对象,该对象随后将别用来调用get()函数。同样,下面的语句将把输入中连续的两行分别读入到数组name1和name2中,其效果与两次调用cin.getline()相同:
cin.getline(name1, Arsize).getline(name2, ArSize);
为什么要使用get(),而不是getline()呢?首先,老式实现没有getline()。其次,get()使输入更仔细。例如,假设用get()将一行读入到数组中。如何知道停止读取的原因是由于已经读取了整行,而不是由于数组已填满呢?查看下一个字符,如果是换行符,说明已经读取了整行;否则,说明该行中还有其他输入。总之,getline()使用起来更简单一些,但get()使得错误检查更简单些。
空行和其他问题
当get()(不是getline())读取空行后将设置失效位。这意味着接下来的输入将被阻断,但可以用下面的命令来恢复输入:
cin.clear();
另一个潜在问题是,输入字符串可能比分配的空间长,如果输入行包含的字符数比指定的多,则getline()和get()将把余下的字符留在输入队列中,而getline()还会设置失效位,并关闭后面的输入。
混合输入字符串和数字
cin读取数字,将回车键生成的换行符留在了输入队列之中。后面的cin.getline()看到换行符后,将认为是一个空行,并将一个空字符串赋给address数组。解决之道是,在读取地址之前先读取并丢弃换行符。这可以通过几种方法来完成,其中包括使用没有参数的get()和使用接受一个char参数的get()。可以单独进行调用:
cin >> year; cin.get();
也可以利用表达式cin>>year返回cin对象,将调用拼接起来:
(cin >> year).get();
string类I/O
下面是将一行输入读取到数组中的代码:
cin.getline(charr, 20);
这种句点表示法表明,函数getline()是istream类的一个类方法,cin作为对象调用getline()方法。
下面是将一行输入读入到string对象中的代码:
getline(cin, str);
这里没有使用句点表示法,这表明这个getlin()不是类方法。它将cin作为参数,指出到哪里去查找输入。另外,也没有指出字符串长度的参数,因为string对象将根据字符串长度自动调整自己的大小。由于在引入string类之前,C++就有istream类,因此istream的设计考虑到了诸如double和int等基本C++数据类型,但没有考虑string类型,所以istream类中,有处理double、int和其他基本类型的类方法,但没有处理string对象的类方法。尽管如此,通过string类的友元函数,可以实现读取字符串到string对象中。
循环和文本输入
如果程序要使用循环来读取来自键盘的文本输入,则必须有办法知道何时停止读取。一种方法是选择某个特殊字符,将其作为停止标记。按下键盘上的键不能自动将字符显示到屏幕上,程序必须通过回显输入字符来完成这项工作。
读取char值时,与读取其他基本类型一样,cin将忽略空格和换行符。
发送给cin的输入被缓冲,这意味着只有在用户按下回车键后,输入的内容才会被发送给程序。
成员函数cin.get(ch)读取输入中的下一个字符(即使它是空格),并将其赋给变量ch。
每次读取一个字符,直到遇到EOF(文件尾)的输入循环的基本设计如下:
cin.get(ch); while (cin.fail() == fales) { ... cin.get(ch); }
可以使用!运算符将上述while测试改写成:
while (!cin.fail())
方法cin.get(char)的返回值是一个cin对象。然而,istream类提供了一个可以将istream对象(如cin)转换为bool值的函数;当cin出现在需要bool值的地方(如在while循环的测试条件中)时,该转换函数将被调用。另外,如果最后一次读取成功了,则转换得到的bool值为true;否则为false。这意味着额可以将上述while测试改写成:
while (cin)
由于cin.get(char)的返回值为cin,因此可以将循环精简成:
while (cin.get(ch)) { ... }
另一个cin.get()版本
不接受任何参数的cin.get()成员函数返回输入中的下一个字符。也就是说,可以这样使用它:
ch = cin.get();
同样,可以使用cout.put()函数来显示字符:
cout.put(ch);
当cin.get()到达EOF时,将没有可返回的字符。相反,cin.get()将返回一个用符号常量EOF表示的特殊值。该常量是在头文件iostream中定义的。
属性 | cin.get(ch) | ch=cin.get() |
传递输入字符的方式 | 赋给参数ch | 将函数返回值赋给ch |
用于字符输入时函数的返回值 | istream对象(执行bool转换后为true) | int类型的字符编码 |
到达EOF时函数的返回值 | istream对象(执行bool转换后为fales) | EOF |
读取数字的循环
程序发现用户输入了错误内容时,应采取三个步骤:
- 重置cin以接受新的输入
- 删除错误输入
- 提示用户再输入
for (i = 0; i < Max; i+) { while (!(cin >> golf[i])) { cin.clear(); while (cin.get() != '\n') continue; cout << "Please enter a number: "; } }
如果输入88,则cin表达式将为true,因此将一个值放到数组中;而表达!(cin>>golf[i])为false,因此结束内部循环。然而,如果输入must i?,则cin表达式将为false,因此不会将任何值放到数组中,而表达式!(cin>>golf[i])为true,因此进入内部的while循环。该循环的第一句使用clear()方法重置输入,如果省略这条语句,程序将拒绝继续读取输入。接下来,程序再while循环中使用cin.get()来读取行尾之前的所有输入,从而删除这一行中的错误输入。另一种方法是读取到下一个空白字符,这样每次删除一个单词,而不是一次删除整行。最后,程序告诉用户,应输入一个数字。
简单文件输入/输出
p190
多个参数
cin.get(ch)或ch=cin.get()函数读取所有的输入字符,包括空格和换行符,而cin>>跳过空格和换行符。
填充数组
for (i = 0; i < limit; i++) { cin >> temp; if (!cin) { cin.clear(); while (cin.get() != '\n') continue; cout << "Bad input\n"; break; } else if (temp < 0) break; ar[i] = temp }
复习
使用cin来控制while循环:
while (cin >> rplace.x >> rplace.y)
cin是istream类的一个对象。抽取运算符(>>)被设计成使得cin>>rplace.x也是一个istream对象。使用cin>>rplace.x时,程序将调用一个函数,该函数返回一个istream值。将抽取运算符用于cin>>rplace.x对象(就像cin>>rplace.x>>rplace.y这样),也将获得一个istream对象。因此整个while虚幻的测试表达式最终结果为cin,而cin被用于测试表达式中时,将根据输入是否成,被转换为bool值true或false。在需要一种不会将某些数值排除在外的、终止循环的方式时,别忘了考虑使用这种方式。另外请记住,错误输入将设置一个错误条件,禁止进一步读取输入。如果程序在输入循环后还需要进行输入,则必须使用cin.clear()重置输入,然后还可能需要通过读取不合法的输入来丢弃他们。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 一个奇形怪状的面试题:Bean中的CHM要不要加volatile?
· [.NET]调用本地 Deepseek 模型
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· .NET Core 托管堆内存泄露/CPU异常的常见思路
· PostgreSQL 和 SQL Server 在统计信息维护中的关键差异
· CSnakes vs Python.NET:高效嵌入与灵活互通的跨语言方案对比
· DeepSeek “源神”启动!「GitHub 热点速览」
· 我与微信审核的“相爱相杀”看个人小程序副业
· Plotly.NET 一个为 .NET 打造的强大开源交互式图表库
· 上周热点回顾(2.17-2.23)