(十)C++学习 | 输入输出流


1. 简介

C + + {\rm C}++ C中,与输入输出相关的类的继承与派生关系如下:
在这里插入图片描述

  • istream是用于输入的流类,我们常用的cin就是该类的对象;
  • ostream是用于输出的流类,我们常用的cout就是该类的对象;
  • ifstream是用于从文件读取数据的类;
  • ofstream是用于向文件下入数据的类;
  • iostream是既能用于输入,又能用于输出的类;
  • fstream是既能从文件读取数据,又能向文件写入数据的类。

2. 标准流对象

我们常用的输入流对象cin和输出流对象cout又称为标准流对象,它们位于命名空间std中。除此之外,还有cerrclog等与标准错误输出设备相连的对象。通常,在缺省的情况下,以下语句完成的功能是一致的:

cerr << "Hello World!" <<endl;
clog << "Hello World!" <<endl;
cout << "Hello World!" <<endl;
  • cin对应于标准输入流,用于从键盘读取数据,也可以被重定向为从文件中读取数据;
  • cout对应于标准输出流,用于向屏幕输出数据,也可以被重定向为向文件写入数据;
  • cerr对应于标准错误数据流,用于向屏幕输出出错信息;
  • clog对应于标准错误数据流,用于向屏幕输出出错信息;
  • cerrclog的区别在于cerr不适用缓冲区,直接向显示器输出信息;而输出到eclog中的信息先会被存放在缓冲区,缓冲区满或者刷新时才输出到屏幕。

下面是输出重定向的例子:

#include<iostream>

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<iostream>

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代表输入流结束。


3. istream类的成员函数

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<iostream>

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,所以缓冲区什么东西也没有接受。

4. 用流操纵算子控制输出格式

用流操纵算子控制输出格式就是规范化输出格式,主要包括以下几点:

  • 整数流的基数:流操纵算子十进制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<iostream>
#include<iomanip>

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<iostream>
#include<iomanip>

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<iostream>
#include<iomanip>

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;

5. 总结

本文介绍了 C + + {\rm C}++ C中的输入输出流,并以从键盘或文件读写为例说明。然后详细介绍了使用流操纵算子控制程序的输出格式,这是我们平时在写程序时常用到的内容。最后,以一个综合的例子来介绍常用到的使用流操纵算子的情景。


参考

  1. 北京大学公开课:程序设计与算法(三)C++面向对象程序设计.


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值