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 << "Enter numbers: ";
    int sum = 0;
    int input;
    try {
        while (cin >> input)
        {
            sum += input;
        }
    } catch(ios_base::failure & bf)
    {
        cout << bf.what() << endl;
        cout << "O! the horror!\n";
    }

    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;
}

在这里插入图片描述
这就是如何在接受输入时使用异常。然而,应该使用它们吗?这取决于具体情况。就这个例子而言,答案是否定的。异常用于捕获不正常的意外情况,但这个例子将输入错误作为一种退出循环的方式。然而。让这个程序在 badbit位被设置时引发异常可能是合理的,因为这种情况是意外的。如果程序被设计成从一个数据文件中读取数据,直到到达文件尾,则在failbit 位被设置时引发异常也是合理的,因为这表明数据文件出现了问题。

3.流状态的影响

只有在流状态良好(所有的位都被清除)的情况下,下面的测试才返回true:

while(cin >> input)

如果测试失败,可以使用表17.4中的成员函数来判断可能的原因。例如,可以将程序清单17.11中的核心部分修改成这样:

while(cin >>input)
sum += input;
if (cin.eof())
cout << "Loop terminated because Eop encountered\n";

设置流状态位有一个非常重要的后果:流将对后面的输入或输出关闭,直到位被清除。例如,下面的代码不可行:

while(cin >> input)
sum += input;
cout <<"Last value entered ="<< input <s endl;
cout <"Sumsu sum <s endl;
cout <<"Now enter anew number:";
cin >> input;won't work

如果希望程序在流状态位被设置后能够读取后面的输入,就必须将流状态重置为良好。这可以通过调用clear()方法来实现:

while(cin >>input)
sum += input;
cout <s"Last value entered="<< input << endl;
cout <Sum=<<sum << endl;
cout <e"Now enter a new number:";
cin.clear();
//reset stream state
while(!isspace(cin.get()))
continue ;ein >> input;
//get rid of bad input//will work now

注意,这还不足以重新设置流状态。导致输入循环终止的不匹配输入仍留在输入队列中,程序必须跳过它。一种方法是一直读取字符,直到到达空白为止。sspace()函数(参见第6章)是一个cctype函数,它在参数是空白字符时返回true。另一种方法是,丢弃行中的剩余部分,而不仅仅是下一个单词:while(cin.get()!=\n’)
continue;//get rid rest of line这个例子假设循环由于不恰当的输入而终止。现在,假设循环是由于到达文件尾或者由于硬件故障而终止的,则处理错误输入的新代码将毫无意义。可以使用fail()方法检测假设是否正确,来修复问题。由于历史原因,fail()在 failbit 或 cofbit 被设置时返回 tue,因此代码必须排除后一种情况。下面是一个排除这种情况的例子:

while (cin >> input)
sum + input;
cout <<"Last value entered="<< input << endl;
Cout <<"Sum="< sum<s endl;
if (cin.fail()&& lcin.eof())// failed because of mismatched input
cin.clear()://reset stream statewhile(!isspace(cin.get()))
continue ;//get rid of bad input
else //else bail out
cout <"Icannot go on!n";
exit(1);
cout < "Now enter a new number:
ein >>input;//will work now

17.3.3 其他istream 类方法

第3章~第5章讨论了 get()和 getline()方法。您可能还记得,它们提供下面的输入功能:方法 get(char&)和 get(void)提供不跳过空白的单字符输入功能:函数 get(char*,int,char)和 getline(char*,int,char)在默认情况下读取整行而不是一个单词。它们被称为非格式化输入函数(unformatted input fimctions),因为它们只是读取字符输入,而不会跳过空白,也不进行数据转换。
来看一下 istream 类的这两组成员函数。

1.单字符输入

在使用 char 参数或没有参数的情况下,get()方法读取下一个输入字符,即使该字符是空格、制表符或换行符。gel(char & ch)版本将输入字符赋给其参数,而 get(void)版本将输入字符转换为整型(通常是 int),并将其返回。
(1)成员函数 get(char&)先来看get(char&)。假设程序中包含如下循环:

int ct =0;
char ch;
cin.get(ch);while(ch !e\n')
cout << ch;
Ct++;
cin.get(ch);
cout <<et <endl;

接下来,假设提供了如下输入:

I C+ clearly.<Enter>

按下回车键后,这行输入将被发送给程序。上述程序片段将首先读取字符1,使用cout显示它,并将ct递增到 1。接着,它读取1后面的空格字符,显示它,并将ct递增到2。这一过程将一直继续下去,自到程序将回车键作为换行符处理,并终止循环。这里的重点是,通过使用ge(ch),代码读取、显示并考虑空格和可打印字符。
假设程序试图使用>>:

int ct = 0;char ch;cin >>ch;while(ch !=\n')
// FAILS
cout <ch;
Ct++;
cin >>ch;
cout <s ct << endl;

则代码将首先跳过空格,这样将不考虑空格,因此相应的输出压缩为如下:

I C++clearly.

更糟糕的是,循环不会终止!由于抽取运算符跳过了换行符,因此代码不会将换行符赋给ch,所以while 循环测试将不会终止循环。
get(char &)成员函数返回一个指向用于调用它的istrcam 对象的引用,这意味着可以拼接 get(char &)后面的其他抽取:

char cl,c2,c3;
cin.get(c1).get(c2)>> c3;

首先,cin.get(c1)将第一个输入字符赋给cl,并返回调用对象–cin。这样代码缩为cin.get(c2)>>c3,它将第二个输入字符赋给c2。该函数调用返回cin,将代码缩为cin>>c3。这将把下一个非空白字符赋给c3。因此c1和c2的值最后为空格,但c3不是。
如果 cin.get(char &)到达文件尾–无论是真正的文件尾,还是通过键盘仿真的文件尾(对于 DOS 和Windows命令提示符模式,为按下 Ct+Z:对于UNIX,是在行首按下 C+D),它都不会给其参数赋值。这是完全正确的,因为如果程序到达文件尾,就没有值可供赋给参数了。另外,该方法还调用sestate(failbit)导致cin 的测试结果为false:

char ch;
while(cin.get(ch))
//process input

只要存在有效输入,cin.get(ch)的返回值都将是cin,此时的判定结果为true,因此循环将继续。到达文件尾时,返回值判定为flse,循环终止。(2)成员函数 gct(void)
gct(void)成员函数还读取空白,但使用返回值来将输入传递给程序。因此可以这样使用它:

int ct =0;
char chi
ch = cin.get();while (ch !=\n')
/use return value
cout << ch;
Cと++;
ch =cin.get();
cout <ct< endl;

get(void)成员函数的返回类型为int(或某种更大的整型,这取决于字符集和区域)。这使得下面的代码是非法的:

char cl,c2,c3;
cin.get().get()>>c3;// not valid

这里,cin.get()将返回一个int 值。由于返回值不是类对象,因此不能对它应用成员运算符。因此将出现语法错误。然而,可以在抽取序列的最后使用get():

char c1:
cin.get(e1).get()://validget

(void)的返回类型为 int,这意味着它后面不能跟抽取运算符。然而,由于 cin.get(cl)返回cin,因此它可以放在 ge()的前面。上述代码将读取第一个输入字符,将其赋给cl,然后读取并丢弃第二个输入字符。
到达文件尾后(不管是真正的文件尾还是模拟的文件尾),cin.gel(void)都将返回值EOF–头文件iostream提供的一个符号常量。这种设计特性使得可以这样来读取输入:

int ch;
while((ch=cin.get())!= EOF)
//process input

这里应将ch的类型声明为int,而不是 char,因为值 EOF可能无法使用 char 类型来表示。
第5章更详细地介绍了这些函数,表17.5对单字符输入函数的特性进行了总结。
在这里插入图片描述
2.采用哪种单字符输入形式
假设可以选择>>、get(char &)或get(void),应使用哪一个呢?首先,应确定是否希望跳过空白。如果跳过空白更方便,则使用抽取运算符>>。例如,提供菜单选项时,跳过空白更为方便:

cout<< "a. annoy clientb。bi1l client\n"
<< "c. calm client
d. deceive client\n"
<< "q.\n";
cout <<"Enter a,b,c,d,org:";
char ch;
cin >>ch;while(ch != 'q')
switch(ch)
4
cout<<"Entera,b,c,d,org:";
cin >>ch;

要输入b进行响应,可以键入b并按回车键,这将生成两个字符的响应–b。如果使用get(),则必须添加在每次循环中处理字符的代码,而抽取运算符可以跳过它(如果使用过C语言进行编程,则可能遇到过使用换行符进行响应为非法的情况。这是个很容易解决的问题,但比较讨厌)。如果希望程序检查每个字符,请使用get()方法,例如,计算字数的程序可以使用空格来判断单词何时结束。在 get()方法中,get(char&)的接口更佳。get(void)的主要优点是,它与标准C语言中的getchar()函数极其类似,这意味着可以通过包含iostream(而不是sidio.h),并用cin.ge()替换所有的 getchar(),用cout.put(ch)替换所有的putchar(ch),来将C程序转换为C++程序。
3.字符串输入:getline()、get()和ignore()
接下来复习一下第4章介绍的字符串输入成员函数。getline()成员函数和get()的字符串读取版本都读取字符串,它们的函数特征标相同(这是从更为通用的模板声明简化而来的):

istream & get(char*,int,char);
istream & get(char *int);
istream & getline(char*,int,char);istream & getline(char*int);

第一个参数是用于放置输入字符串的内存单元的地址。第二个参数比要读取的最大字符数大1(额外的一个字符用于存储结尾的空字符,以便将输入存储为一个字符串)。第3个参数指定用作分界符的字符,只有两个参数的版本将换行符用作分界符。上述函数都在读取最大数目的字符或遇到换行符后为止。例如,下面的代码将字符输入读取到字符数组Iine中:

char line[50];
cin.get(line,50);

cin.get()函数将在到达第 49 个字符或遇到换行符(默认情况)后停止将输入读取到数组中。gct()和getline()之间的主要区别在于,get()将换行符留在输入流中,这样接下来的输入操作首先看到是将是换行符,而 gerline()抽取并丢弃输入流中的换行符。
第4章演示了如何使用这两个成员函数的默认格式。现在来看一下接受三个参数的版本,第三个参数用于指定分界符。遇到分界字符后,输入将停止,即使还未读取最大数目的字符。因此,在默认情况下,如果在读取指定数目的字符之前到达行尾,这两种方法都将停止读取输入。和默认情况一样,ge()将分界字符留在输入队列中,而 getline()不保留。
程序清单17.13演示了 getline()和 get()是如何工作的,它还介绍了ignore()成员函数。该函数接受两个参数:一个是数字,指定要读取的最大字符数:另一个是字符,用作输入分界符。例如,下面的函数调用读取并丢弃接下来的255个字符或直到到达第一个换行符:ein.ignore(255,n’);
原型为两个参数提供的默认值为1和EOF,该函数的返回类型为istream&:istream &ignore(int =1.int = EOF);
默认参数值 EOF 导致ignore()读取指定数目的字符或读取到文件尾。
该函数返回调用对象,这使得能够拼接函数调用,如下所示:cin.ignore(255,n’).ignore(255,\n’);其中,第一个ignore()方法读取并丢弃一行,第二个调用读取并丢弃另一行,因此一共读取了两行。现在来看一看程序清单17.13。

程序清单17.13 get_fun.cpp

// get_fun.cpp -- using get() and getline()
#include <iostream>
const int Limit = 255;

int main()
{
    using std::cout;
    using std::cin;
    using std::endl;

    char input[Limit];

    cout << "Enter a string for getline() processing:\n";
    cin.getline(input, Limit, '#');
    cout << "Here is your input:\n";
    cout << input << "\nDone with phase 1\n";

    char ch;
    cin.get(ch);
    cout << "The next input character is " << ch << endl;

    if (ch != '\n')
        cin.ignore(Limit, '\n');    // discard rest of line

    cout << "Enter a string for get() processing:\n";
    cin.get(input, Limit, '#');
    cout << "Here is your input:\n";
    cout << input << "\nDone with phase 2\n";

    cin.get(ch);
    cout << "The next input character is " << ch << endl;
/* keeping output window open */
/*
    cin.clear();
    while (cin.get() != '\n')
        continue;
    cin.get();
*/
    return 0; 
}

在这里插入图片描述
注意,getline()函数将丢弃输入中的分界字符#,而get()函数不会。
$ 4.意外字符串输入
get(char*,int)和 gctline()的某些输入形式将影响流状态。与其他输入函数一样,这两个函数在遇到文件尾时将设置 eofbit,遇到流被破坏(如设备故障)时将设置badbit。另外两种特殊情况是无输入以及输入
到达或超过函数调用指定的最大字符数。下面来看这些情况。对于上述两个方法,如果不能抽取字符,它们将把空值字符放置到输入字符串中,并使用setstate()设置failbit。方法在什么时候无法抽取字符呢??一种可能的情况是输入方法立刻到达了文件尾。对于get(char*,int)来说,另一种可能是输入了一个空行:

char temp[80];
while (cin.get(temp,80))//terminates on empty line

有意思的是,空行并不会导致 getline()设置failbit。这是因为 getline()仍将抽取换行符,虽然不会存储如果希望 getline()在遇到空行时终止循环,则可以这样编写:char temp[80];while(cin.getline(temp,80)&& temp[0]!\0’)// terminates on empty line
现在假设输入队列中的字符数等于或超过了输入方法指定的最大字符数。首先,来看getine()和下面的代码:

char temp[30];
while (cin.getline(temp,30))

getline()方法将从输入队列中读取字符,将它们放到temp数组的元素中,直到(按测试顺序)到达文件尾、将要读取的字符是换行符或存储了 29个字符为止。如果遇到文件尾,则设置cofbit;如果将要读取的字符是换行符,则该字符将被读取并丢弃;如果读取了29个字符,并且下一个字符不是换行符,则设置failbit。因此,包含30个或更多字符的输入行将终止输入。
现在来看 get(char*,int)方法。它首先测试字符数,然后测试是否为文件尾以及下一个字符是否是换行符。如果它读取了最大数目的字符,则不设置faibit 标记。然而,由此可以知道终止读取是否是由于输入字符过多引起的。可以用 peek()(参见下一节)来查看下一个输入字符。如果它是换行符,则说明 get()已读取了整行:如果不是换行符,则说明get()是在到达行尾前停止的。这种技术对getline()不适用,因为getline()读取并丢弃换行符,因此查看下一个字符无法知道任何情况。然而,如果使用的是get(),则可以知道是否读取了整个一行。下一节将介绍这种方法的一个例子。另外,表17.6总结了这些行为。
17.3.4 其他istream 方法
除前面讨论过的外,其他istream方法包括read()、peck()、gcount()和putback()。read( )函数读取指定数目的字节,并将它们存储在指定的位置中。例如,下面的语句从标准输入中读取144个字符,并将它
们存储在 gross数组中:

char gross[144];
cin.read(gross,144);

与 getline()和 get( )不同的是,read()不会在输入后加上空值字符,因此不能将输入转换为字符串。read()方法不是专为键盘输入设计的,它最常与ostream write()函数结合使用,来完成文件输入和输出。该方法的返回类型为istream&,因此可以像下面这样将它拼接起来:

char gross[144];
char score[20];
cin.read(gross,144).read(score,20);

peck()函数返回输入中的下一个字符,但不抽取输入流中的字符。也就是说,它使得能够查看下一个字符。假设要读取输入,直到遇到换行符或句点,则可以用 peek()查看输入流中的下一个字符,以此来判断是否继续读取:

char great input[80];
char ch;
int i = 0;
while((ch=cin.peek())!:&& ch !=\n')cin.get(great input[i++]);great input [i]=\0';

cin.peek()查看下一个输入字符,并将它赋给ch。然后,while 循环的测试条件检査 ch是否是句点或换行符。如果是,循环将该字符读入到数组中,并更新数组索引。当循环终止时,句点和换行符将留在输入流中,并作为接下来的输入操作读取的第一个字符。然后,代码将一个空值字符放在数组的最后,使之成
为一个字符串。gcount()方法返回最后一个非格式化抽取方法读取的字符数。这意味着字符是由get()、getline()、ignore()或 read()方法读取的,不是由抽取运算符(>>)读取的,抽取运算符对输入进行格式化,使之与特定的数据类型匹配。例如,假设使用cin.get(myarray,80)将一行读入myarray数组中,并想知道读取了多少个字符,则可以使用 strlen()函数来计算数组中的字符数,这种方法比使用cin.gcount()计算从输入流中读取了多少字符的速度要快。
putback()函数将一个字符插入到输入字符串中,被插入的字符将是下一条输入语句读取的第一个字符。putback()方法接受一个char 参数–要插入的字符,其返回类型为istream&,这使得可以将该函数调用与其他 istream 方法拼接起来。使用peek()的效果相当于先使用ge()读取一个字符,然后使用putback()将该字符放回到输入流中。然而,putback()允许将字符放到不是刚才读取的位置。程序清单17.14采用两种方式来读取并显示输入中#字符(不包括)之前的内容。第一种方法读取#字符,然后使用 putback()将它插回到输入中。第二种方法在读取之前使用 peek()查看下一个字符。

程序清单17.14 peeker.Cpp

// peeker.cpp -- some istream methods
#include <iostream>

int main()
{
    using std::cout;
    using std::cin;
    using std::endl;

//  read and echo input up to a # character
    char ch;

    while(cin.get(ch))          // terminates on EOF
    {
        if (ch != '#')
            cout << ch;
        else
        {
            cin.putback(ch);    // reinsert character
            break;
        }
    }

    if (!cin.eof())
    {
        cin.get(ch);
        cout << endl << ch << " is next input character.\n";
    }
    else
    {
        cout << "End of file reached.\n";
        std::exit(0);
    }

    while(cin.peek() != '#')    // look ahead
    {
        cin.get(ch);
        cout << ch;
    }
    if (!cin.eof())
    {
        cin.get(ch);
        cout << endl << ch << " is next input character.\n";
    }
    else
        cout << "End of file reached.\n";
// keeping output window open 
/*
    cin.clear();
    while (cin.get() != '\n')
        continue;
    cin.get();
*/
    return 0; 
}

在这里插入图片描述
程序说明
来详细讨论程序清单17.14中的一些代码。第一种方法是用while循环来读取输入:

while(cin.get(ch))
//terminates on EOF
if (ch != ·#”)
cout <<ch;
else
cin.putback(ch);//reinsert characterbreak;

达到文件尾时,表达式(cin.get(ch))将返回false,因此从键盘模拟文件尾将终止循环。如果#字符首先出现,则程序将该字符放回到输入流中,并使用break语句来终止环。
第二种方法看上去更简单:

while(cin.peek()!=#')
// look ahead
cin.get(ch);
cout << ch;

程序查看下一个字符。如果它不是#,则读取并显示它,然后再查看下一个字符。这一过程将一直继续下去,直到出现分界字符。
现在来看一个例子(参见程序清单17.15),它使用peek()来确定是否读取了整行。如果一行中只有部分内容被加入到输入数组中,程序将删除余下的内容。

程序清单 17.15 truncate.cpp

// truncate.cpp -- using get() to truncate input line, if necessary
#include <iostream>
const int SLEN = 10;
inline void eatline() { while (std::cin.get() != '\n') continue; }
int main()
{
    using std::cin;
    using std::cout;
    using std::endl;

    char name[SLEN];
    char title[SLEN];
    cout << "Enter your name: ";
    cin.get(name,SLEN);
    if (cin.peek() != '\n')
        cout << "Sorry, we only have enough room for "
                << name << endl;
    eatline();
    cout << "Dear " << name << ", enter your title: \n";
    cin.get(title,SLEN);
    if (cin.peek() != '\n')
        cout << "We were forced to truncate your title.\n";
    eatline();
    cout << " Name: " << name
         << "\nTitle: " << title << endl;
    // cin.get();
    return 0; 
}

在这里插入图片描述
注意,下面的代码确定第一条输入语句是否读取了整行:

while(cin.get()!=n')continue;

如果 gct()读取了整行,它将保留换行符,而上述代码将读取并丢弃换行符。如果get()只读取一部分,则上述代码将读取并丢弃该行中余下的内容。如果不删除余下的内容,则下一条输入语句将从第一个输入行中余下部分的开始位置读取。对于这个例子,这将导致程序把字符串snifer 读取到 title数组中。

  • 18
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值