循环和文本输入
逐字符的读取来自文件或键盘的文本。
1. 使用原始的cin进行输入
如果程序要使用循环来读取来自键盘的文本输入,则必须有办法知道何时停止读取。
一种方法是选择某个特殊字符——有时被称为哨兵字符(sentinel character),将其作为停止标记。
程序5.16
#include<iostream>
int main()
{
using namespace std;
char ch;
int count = 0;
cout << "Enter characters; enter # to quit:\n";
cin >> ch;
while (ch != '#')
{
cout << ch;
++count;
cin >> ch;
}
cout << endl << count << " characters read\n";
system("pause");
return 0;
}
程序5.16在遇到#时停止读取输入。该程序计算读取的字符数,并回显这些字符,即在屏幕上显示读取的字符。按下键盘上的键不能自动将字符显示到屏幕上,程序必须通过回显输入字符来完成这项工作。
程序在循环之前读取第一个第一个输入字符,这样循环可以测试第一个字符,因为第一个字符可能是#,这样程序能够正确地跳过整个循环。
如果读取到的第一个字符不是#,则程序进入该循环,显示字符,增加计数,然后读取下一个字符。
最后一步是极为重要的,没有这一步,循环将反复处理第一个输入字符,一直进行下去。有了这一步后,程序就可以处理到下一个字符。
结束循环的条件是最后读取的一个字符是#。该条件是通过在循环之前读取一个字符进行初始化的,而通过循环体结尾读取下一个字符进行更新。
程序在输出时省略了空格。cin在读取char值时,与读取其他基本类型一样,将忽略空格和换行符,因此输入中的空格没有被回显,也没有被包括在计数内。
发送给cin的输入被缓冲,只有在按下回车键后,输入的内容才会被发送给程序。这就是在运行该程序时,可以在#后面输入字符的原因。按下回车键后,整个字符序列将被发送给程序,但程序在遇到#字符后将结束对输入的处理。
2.使用cin.get(char)进行补救
通常,逐个字符读取输入的程序需要检查每个字符,包括空格、制表符和换行符。cin所述的istream类中包含一个能够满足这种要求的成员函数cin.get()。cin.get(ch)读取输入的下一个字符(即使它是空格),并将其赋给变量ch。
程序5.17
#include<iostream>
int main()
{
using namespace std;
char ch;
int count = 0;
cout << "Enter characters; enter # to quit:\n";
cin.get(ch);
while (ch != '#')
{
cout << ch;
++count;
cin.get(ch);
}
cout << endl << count << " characters read\n";
system("pause");
return 0;
}
程序5.17回显了每个字符,并将全部字符计算在内,包括空格。
3.文件尾条件
如果输入来自文件,可以使用一种功能更强大的技术——检测文件尾(EOF)——来表示输入结束。
很多操作系统都支持重定向(<),允许用文件替换键盘输入,还允许通过键盘来模拟文件尾条件。
如果编程环境能够检测到EOF,则可以在程序中使用重定向的文件。检测到EOF后,cin将两位(eofbit和failbit)都设置为1。可以通过成员函数eof()来查看eofbit是否被设置;如果检测到EOF,则cin.eof()将返回bool值true,否则返回false。同样,如果eofbit或failbit被设置为1,则fail()成员函数返回true,否则返回false。
程序5.18
#include<iostream>
int main()
{
using namespace std;
char ch;
int count = 0;
cin.get(ch);
while (cin.fail() == false)
{
cout << ch;
++count;
cin.get(ch);
}
cout << endl << count << " characters read\n";
system("pause");
return 0;
}
在windows系统上运行该程序,因此用【Ctrl+Z】和回车键来模拟EOF条件。
通过使用重定向,可以用该程序来显示文本文件,并报告它包含的字符数。
1. EOF结束输入
cin检测到EOF时,将设置cin对象中一个指示EOF条件的标记。设置这个标记后,cin将不读取输入,再次调用cin也不管用。
2.常见的字符输入做法
每次读取一个字符,直到遇到EOF的输入循环的基本设计如下:
cin.get(ch);
while (cin.fail() == false)
{
...
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.fail()或!cin.eof()更通用,因为它可以检测到其他失败原因,如磁盘故障。
由于cin.get(char)的返回值为cin,因此可以将循环精简为:
while (cin.get(ch))
{
...
}
这样,cin.get(char)只被调用一次,而不是两次(循环前一次、循环结束后一次)。
4. 另一个cin.get()版本
不接受任何参数的cin.get()成员函数返回输入中的下一个字符,因此,可以这样使用它:
ch = cin.get();
该函数的工作方式与C语言中的getchar()相似,将字符编码作为int值返回;而cin.get(ch)返回一个对象,而不是读取的字符。
同样,可以使用cout.put()参数来显示字符:
cout.put(ch);
该函数的工作方式类似于C语言中的putchar(),只不过其参数类型为char,而不是int。
程序5.19
#include<iostream>
int main(void)
{
using namespace std;
int ch;
int count = 0;
while ((ch = cin.get()) != EOF)
{
cout.put(char(ch));
++count;
}
cout << endl << count << " characters read\n";
system("pause");
return 0;
}
下表总结了cin.get(ch)和cin.get()的差别:
属性 | cin.get(ch) | ch=cin.get() |
---|---|---|
传递输入字符的方式 | 赋值给参数ch | 将函数返回值赋给ch |
用于字符输入时函数的返回值 | istream对象(执行bool转换后为true) | int类型的字符编码 |
到达EOF时函数的返回值 | istream对象(执行bool转换后为false) | EOF |