在 C + + {\rm C}++ C++中,与输入输出相关的类的继承与派生关系如下:
istream
是用于输入的流类,我们常用的cin
就是该类的对象;ostream
是用于输出的流类,我们常用的cout
就是该类的对象;ifstream
是用于从文件读取数据的类;ofstream
是用于向文件下入数据的类;iostream
是既能用于输入,又能用于输出的类;fstream
是既能从文件读取数据,又能向文件写入数据的类。我们常用的输入流对象cin
和输出流对象cout
又称为标准流对象,它们位于命名空间std
中。除此之外,还有cerr
、clog
等与标准错误输出设备相连的对象。通常,在缺省的情况下,以下语句完成的功能是一致的:
cerr << "Hello World!" <<endl;
clog << "Hello World!" <<endl;
cout << "Hello World!" <<endl;
cin
对应于标准输入流,用于从键盘读取数据,也可以被重定向为从文件中读取数据;cout
对应于标准输出流,用于向屏幕输出数据,也可以被重定向为向文件写入数据;cerr
对应于标准错误数据流,用于向屏幕输出出错信息;clog
对应于标准错误数据流,用于向屏幕输出出错信息;cerr
和clog
的区别在于cerr
不适用缓冲区,直接向显示器输出信息;而输出到eclog
中的信息先会被存放在缓冲区,缓冲区满或者刷新时才输出到屏幕。下面是输出重定向的例子:
#include
using namespace std;
int main() {
int x, y;
// 从键盘输入x和y两个值
cin >> x >> y;
// 将标准输出重定向到test.txt文件中
freopen("test.txt", "w", stdout);
// 除数为零,则在屏幕上输出错误信息
if (y == 0) {
cerr << "error." << endl;
}
// 否则将计算结果存放到test.txt文件中
else
{
cout << x / y;
}
return 0;
}
下面是输入重定向的例子:
#include
using namespace std;
int main() {
double f; int n;
// 将标准输入流重定向到test.txt文件中
freopen("test.txt", "r", stdin);
// 从文件中获取输入内容
cin >> f >> n;
// 输出重定向后的内容,如test.txt中的内容为3.14 314,
// 则词条语句打印结果为3.14,314
cout << f << "," << n << endl;
return 0;
}
上面提到输入流可以来源于键盘或文件,针对这两种方式的判断输入流结束的方式如下:
int x;
while (cin >> x) {
...
}
如果是从文件输入,那么读取到文件尾部时上面循环被打破,此时输入流就算结束;如果从键盘输入,则在单独一行输入ctrl + z
代表输入流结束。
istream
类是用于控制输入流的类,它有几个重要成员函数。首先是重载成员函数getline
。第一种重载形式如下:
istream& getline(char* buf, int bufSize);
从输入流中读取bufSize - 1
个字符到缓冲区buf
,或读取时碰到\n
时提前结束。第二种重载形式如下:
istream& getline(char* buf, int bufSize, char delim);
从输入流中读取bufSize - 1
个字符到缓冲区buf
,或读取时碰到字符delim
时提前结束。两个函数都会自动在buf
中读入数据的结尾添加\0
。\n
或字符delim
都不会被读入buf
,但会被从输入流中取走(删除)。如果输入流中的\n
或字符delim
之前的字符个数达到或超过了bufSize
个,就会导致读入出错。其结果就是:虽然本次读入已经完成,但是之后的读入就会失败了。istream
类的其他几个成员函数:
bool eof();
判断输入流是否结束;int peek();
返回下一个字符,但不从流中去掉;istream& putback(char c);
将字符ch
放回输入流;istream& ignore(int nCount=1, int delim=EOF);
从流中删除最多nCount
个字符,遇到EOF
时结束。#include
using namespace std;
int main() {
int x;
// 100大小的缓冲区
char buf[100];
// 读入x
cin >> x;
// 从输入流中读取89个字符到缓冲区buf,或
// 碰到\n时提前结束
cin.getline(buf, 90);
cout << buf << endl;
return 0;
}
12 abcd\n
,那么屏幕输出结果为空格abcd
。第一部分输入12
被存储在变量x
中;第二部分输入空格abcd
被存储在输入缓冲区中,这时缓冲区没有满,只是遇到回车自动结束。12\n
,那么屏幕什么也不输出。这是由于cin.geline()
一来就遇到了停止符\n
,所以缓冲区什么东西也没有接受。用流操纵算子控制输出格式就是规范化输出格式,主要包括以下几点:
dec
、八进制oct
、十六进制hex
、任意进制setbase
;(precision, setprecision)
;setw, width
;在使用流操纵算子前,需要引入头文件iomanip
。如整数流的基数用法如下:
int n = 10;
cout << n <<endl; // 输出10,十进制
cout << hex << n << endl; // 输出a,十六进制
cout << dec<< n << endl; // 输出10,十进制
cout << oct<< n << endl; // 输出12,八进制
控制浮点数精度的流操纵算子:
cout.precision(5);
cout << setprecision(5);
上面两条语句的功能相同,在以非定点方式(小数点可能不位于个数的右边,如科学计数法。缺省)输出时,指定输出浮点数的有效位数;在以定点方式(小数点一定位于个位数的右边,即我们平常所使用的浮点数的标准表示形式)输出时,指定输出浮点数的小数点后的有效位数。如:
#include
#include
using namespace std;
int main() {
double x = 1234567.89, y = 12.345678;
int n = 123456;
int m = 123;
cout << setprecision(6) << x << endl; // 输出1.23457e+06
cout << y << endl; // 输出12.3457
cout << n << endl; // 输出123456
cout << m << endl; // 输出123
}
对于第一条语句的输出,由于非定向方式为缺省情况,所以输出需要对1234567.89
保留六位有效数字,由于整数部分已经超过六位,这里只能使用科学计数法表示。下面是以定点方式输出保留小数点后六位的结果:
#include
#include
using namespace std;
int main() {
double x = 1234567.89, y = 12.345678;
int n = 123456;
int m = 123;
cout << setiosflags(ios::fixed) << setprecision(6) << x << endl;// 输出1234567.890000
cout << y << endl; // 输出12.345678
cout << n << endl; // 输出123456
cout << m << endl; // 输出123
}
定点输出方式向非定点输出方式的转变:
double x = 1234567.89;
cout << setiosflags(ios::fixed) << setprecision(6) << x << endl; // 定点方式输出
cout << resetiosflags(ios::fixed) << x << endl; // 取消以定点方式输出
设置域宽即设置输出内容所占的宽度,宽度设置的有效性是一次性,在每次输入和输出之前都要设置宽度。如:
int main() {
int w = 4;
char string[10];
cin.width(5); // 设置输入域宽为5,包含结尾字符\0
while (cin >> string) // 从键盘输入1234567890,此时string只保存了1234四个字符
{
cout.width(w++); // 第一次输出域宽为4,第二次输出域宽为5,第三次输出域宽为6
cout << string << endl; // 第一次输出1234,第二次输出 5678,第三次输出 90,位数不足用空格补齐
cin.width(5); // 设置输入域宽为5,包含结尾\0,实际上接受四个字符
}
}
最后来看一个流操纵算子的综合例子:
#include
#include
using namespace std;
int main() {
int n = 141;
double x = 1234567.89, y = 12.34567;
// 分别以十六进制、十进制、八进制先后输出n,即8d 141 215
cout << "1)" << hex << n << " " << dec << n << " " << oct << n << endl;
// 保留五位有效数字
cout << "2)" << setprecision(5) << x << " " << y << " " << endl;
// 保留小数点后面五位
cout << "3)" << fixed << setprecision(5) << x << " " << y << " " << endl;
// 科学计数法输出,且保留小数点后面五位
cout << "4)" << scientific << setprecision(5) << x << " " << y << endl;
// 非负数显示正号,输出宽度为12,宽度不足用*补齐
cout << "5)" << showpos << fixed << setw(12) << setfill('*') << 12.1 << endl;
// 非负数不显示正号,输出宽度为12,宽度不足则右边用填充字符填充
cout << "6)" << noshowpos << setw(12) << left << 12.1 << endl;
// 输出宽度为12,负号和数值分列左右,中间用填充字符填充
cout << "8)" << setw(12) << internal << -12.1 << endl;
cout << "9)" << 12.1 << endl;
return 0;
}
输出如下:
同时,除了 C + + {\rm C}++ C++提供的流操纵算子,用户也可以自定义流操纵算子。自定义流操纵算子的过程类似于函数的重载,如:
ostream& tab (ostream& output) {
return coutput << '\t';
}
定义如上函数后,在输出格式中tab
相当于一个制表符\t
。如下:
// 输出为aa bb
cout << "aa" << tab << "bb" << endl;
本文介绍了 C + + {\rm C}++ C++中的输入输出流,并以从键盘或文件读写为例说明。然后详细介绍了使用流操纵算子控制程序的输出格式,这是我们平时在写程序时常用到的内容。最后,以一个综合的例子来介绍常用到的使用流操纵算子的情景。