输入和输出是贯穿于任何一个程序的重要操作。通过前面小节中的例子,读者不难发现,C语言中的printf不见了,C++中取而代之的是cout。尽管printf在C++中同样有效,但是标准C++推荐使用cout。这是一个非常重要的变化。因为C++中的输入和输出是基于流的概念的。在C++程序中,数据可以从键盘流如到程序中,也可以从程序中流向屏幕或磁盘文件。这种从某个地方流向另外一个地方的一个位序列就是所谓的流。输出的过程就是从一个程序向一个设备移动数据位的过程。这个设备可能是一个显示器,一个打印机甚至是一个文件或者其它某个硬件设备。输入的过程刚好相反。输入的过程是从一个设备(例如键盘、文件、网络连接)向一个程序移动数据位的过程。
来看下面这段程序代码。该段程序中的第一个流数据位于程序的第9行,即文本字符串“请输入您的姓名: ”中的字符,该数据流动流动方向是从程序到一个设备(控制台)。
#include <string>
#include <cstdlib>
#include <iostream>
using namespace std;
void main() {
cout << "请输入您的姓名: ";
string name;
cin >> name;
cout << "Hello " << name;
}
基于输出的流操作使用操作符“<<”来表示数据是要向流写的。该行代码中使用的流是被cout对象引用的输出流,这个对象是一个流类型的对象,cout是ostream类的对象。在实现了一个流输出之后,该程序又从键盘接收了一个数据流,该数据流中储存着用户输入的本文信息。流输入操作使用操作符“>>”来指定一个变量,该变量表示程序应当将其读入的数据留存储在何处。该行代码中使用的流是被cin对象引用的输入流,这个对象也是一个流类型的对象,cin是istream类的对象。cin可以接受输入的一系列字符,并自动略过其中的空格、tab、换行符等空白符。遇到文件结束符时返回0,但并不把这个0值放到输入对象中。最后,程序将数据流输出到控制台。读者应当注意到了,在最后的一个语句中多个数据片段被放在同一个流中,在C++中,这是可行的。
我们可以打开并使用流来从设备中读数据抑或向设备中写数据,例如一个程序可以使用一个文件输出流向文件系统写数据,此外,通过 sockets进行的网络通信也是基于流的。但是在每个C++程序的生命周期中,有三个特殊的流是始终可用的。也就是说程序员无须显式地打开或者关闭这三个流。因为当程序开始执行时,这三个流就自动可用,这种自动加载时通过它们相应的对象来完成的。然而,程序员必须显式地打开或者关闭其它所有输入/输出流。这三个流是标准输入流、标准输出流和标准错误流。这三个标准流均有其各自特殊的用法。标准输入流从控制台中读取数据,标准输出流向控制台写数据,而标准错误流则向控制台输出错误信息。
程序员通过一组对象来访问标准流。对象cin和cout分别提供对于标准输入和输出流的访问,对象cerr 提供的是对于标准错误流的访问。下面这个程序说明了三种标准流的用法。
#include <string>
#include <cstdlib>
#include <iostream>
using namespace std;
void main()
{
cout << "请输入您的姓名和年龄: ";
string name;
int age;
cin >> name >> age;
if (age < 0)
cerr << "\n错误!请确认后重试。";
else
cout << "\n" << name << " is " << age;
}
C++ 程序员可以定义他们创建的类如何使用操作符“<<”和“>>”来与流进行交互。这种操作被称为重载,有关重载的话题将在本章后续内容中介绍,这里不再赘述。
前面已经说过cin是istream类的对象,那么cin同样具有一些方法,这些方法可以增强对于输入操作的控制,下面对cin的一些方法进行简要的介绍。先来看一段示例代码。
#include <cstdlib>
#include <iostream>
using namespace std;
void main()
{
char buf[20];
char tmp;
cin.get(tmp);
cout<<tmp<<endl;
cout<<"请输入一段文本: \n";
cin.ignore(5);
cin.getline(buf,10);
cout<<buf<<endl;
}
程序的运行结果如下:
0
0
请输入一段文本:
123456789123456789
567891234
可见cin.get()接收一个输入字符(包括空白符),并返回该字符值,特别地当它遇到文件结束符时,返回EOF(即这个文件结束符)。所以当程序接受一个输入字符‘0’时,即将其输出。而cin.ignore(int n)的作用则是忽略输入的前n个字符,所以最后输出的流是从字符‘5’开始的,注意最开始输出的字符‘0’也被计入在内。而cin.getline()则是接受输入的一行字符,且可以通过参数控制截取的字符数量。
再来看一个例子,注意代码中略去了头文件,头文件同上例。
void main()
{
char p;
cout<<"请输入一段文本: \n";
while(cin.peek() != '\n')
cout<<(p=cin.get());
cout<<endl;
}
程序的运行结果如下:
请输入一段文本:
Hello World!
Hello World!
该段程序使用了peek这个方法,该方法接收一个字符,并把多余的除换行符以外的输入都显示出来。
read和write也是cin中比较有用的方法,为了说明它们的作用,下面同样给出了一段示例代码,注意其中略去了头文件,头文件同上例。
void main()
{
const int SIZE = 50;
char buf[SIZE];
cout<<"请输入一段文本:\n";
cin.read(buf,20);
cout<<cin.gcount();
cout<<"输入的文本信息是:\n";
cout.write(buf,20);
cout<<endl;
}
该程序的运行结果这里不再给出,请读者自己编译并运行后查看结果。通过程序的运行结果可以看出,read方法接收指定数目的字符到数组中(可读入空白符),提前结束的话输入文件结束符(Ctrl+Z)。write方法从数组中输出指定数目的字符,若这个数目大于数组长度,则输出整个数组。此外,该程序中也使用了gcount方法,它的作用是统计刚才用read读入的字符数目。
--------------------------------------------------