《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()重置输入,然后还可能需要通过读取不合法的输入来丢弃他们。

 

posted @ 2019-08-22 16:24  溪嘉嘉  阅读(693)  评论(0编辑  收藏  举报