流类简介
C++中凡是数据从一个地方传输到另一个地方的操作都是流的操作。因此,一般意义下的读操作在流数据抽象中被称为(从流中)“提取”,写操作被称为(向流中)“插入”。
iostream
流类库的类关系图如下:
图中箭头代表派生关系。ios
是抽象基类,提供输入/输出所需的公共操作,它派生出两个类istream
和ostream
。为了避免多重继承的二义性,从ios
派生istream
和ostream
时,均使用了virtual
关键字(虚继承)。
stream
类提供了流的大部分输入操作,对系统预定义的所有输入流重载提取运算符>>
。ostream
类对系统预定义的所有输出流重载插入运算符<<
。
C++的iostream
类库提供了数百种I/O
功能,iostream
类库的接口部分包含在几个头文件中。常见的头文件有以下3个:
-
iostream
:包含操作所有输入/输出流所需要的基本信息,因此大多数C++程序都应包含这个头文件。该文件含有4个标准流对象,提供了无格式化和格式化的I/O
功能。 -
iomanip
:包含格式化I/O
的带参数流操纵符,可用于指定数据输入/输出的格式。 -
fstream
:包含处理文件的有关信息,提供建立文件、读/写文件的各种操作接口。
标准流对象
C++在头文件iostream
中为了用户预定义了4个标准流对象,分别是:
-
cin
(标准输入流),cin
与标准输入设备(键盘)相关联,用于读取数据,可以被重定向为从文件中读取数据。 -
cout
(标准输出流),cout
与标准输出设备(显示器)相关联,用于输出数据,可以被重定向为向文件里写入数据。 -
cerr
(非缓冲错误输出流),cerr
与标准错误信息输出设备(显示器)相关联(非缓冲),用于输出出错信息,不能被重定向。 -
clog
(缓冲错误输出流),clog
与标准错误信息输出设备相关联(缓冲),用于输出出错信息,不能被重定向。
在实际中,cin
常用于从键盘输入数据,是流类istream
的对象。cout
常用于向屏幕输出数据,是流类ostream
的对象。
#include <iostream>
using namespace std;
int main() {
int x, y;
cin >> x >> y;
//函数`freopen()`的功能是将`stream`按`mode`指定的模式重定向到路径`path`指向的文件。
//将标准输出定向到文件test.txt
freopen("test.txt", "w", stdout);
if (y == 0) {
cerr << "error" << endl;
} else {
cout << x << " / " << y << " = " << x / y << endl;
}
return 0;
};
控制I/O
格式
C++进行I/O
格式控制的方式一般有使用流操纵符、设置标志字和调用成员函数。
流操纵符
流操纵符 | 作用 |
---|---|
endl | 输出一个新行符,并清空流 |
ends | 输出字符串结束,并清空流 |
flush | 清空流缓冲区 |
dec*(默认) | 以十进制形式输入/输出整数 |
hex | 以十六进制形式输入或输出整数 |
cot | 以八进制形式输入或输出整数 |
ws | 提取空白字符 |
fixed | 以普通小数形式输出浮点数 |
scientific | 以科学计数法形式输出浮点数 |
left | 左对齐,即在宽度不足时将填充字符添加到右边 |
right* | 右对齐,即在宽度不足时将填充字符添加到左边 |
setbase(int b) | 设置输出整数时的进制,b为8、10、16 |
setw(int w) | 指定输出宽度为w个字符,或输入字符串时读入w个字符。 一次有效。 |
setfill(int c) | 在指定输出宽度的情况下, 输出的宽度不足时用 ASCⅡ码 为c的字符填充(默认情况是用空格填充) |
setprecision(int n) | 设置输出浮点数的精度为n 。在使用非 fixed 且非scientific 方式输出的情况下,n 即为有效数字最多的位数。如果有效数字位数超过 n ,则小数部分四舍五入, 或自动变为科学计数法输出并保留一共 n 位有效数字;在使用 fixed 方式和scientific 方式输出的情况下,n 是小数点后面应保留的位数。 |
setiosflags(fmtflags f) | 通用操纵符。将格式标志f 所对应的格式标志位置为1
|
resetiosflags(fmtflags f) | 通用操纵符。将格式标志f 所对应的格式标志位置为0 (清除) |
boolapha | 把ture 和false 输出为字符串 |
noboolapha* | 把ture 和false 输出为1 和0
|
showbase | 输出表示数值进制的前缀 |
noshowbase* | 不输出表示数值进制的前缀 |
showpoint | 总是输出小数点 |
noshowpoint* | 只有当小数部分存在时才显示小数点 |
showpos | 在非负数值中显示+
|
noshowpos* | 在非负数值中不显示+
|
#include <iostream>
#include <iomanip>
using namespace std;
int main() {
int n = 65535, m = 20;
//1.分别输出一个整数的十进制、十六进制和八进制表示
cout << "1)" << n << " = " << hex << n << " = " << oct << n << endl;
//1)65535 = ffff = 177777
//2.使用setbase分别输出一个整数的十进制、十六进制和八进制表示
cout << "2)" << setbase(10) << m << " = " << setbase(16) << m << " = " << setbase(8) << m << endl;
//2)20 = 14 = 24
//3.使用showbase和setbase分别输出一个整数的十进制、十六进制和八进制表示
cout << "3)" << showbase;//输出表示数值进制的前缀
cout << setbase(10) << m << " = " << setbase(16) << m << " = " << setbase(8) << m << endl;
//3)20 = 0x14 = 024
return 0;
};
标志字
标志常量名 | 值 | 含义 | 输入/输出 |
---|---|---|---|
ios::skipws | 0x0001 | 跳过输入中的空白 | I |
ios::left | 0x0002 | 按输出域左对齐,用填充字符填充右边 | O |
ios::right* | 0x0004 | 按输入域右对齐,用填充字符填充做左边 | O |
ios::internal | 0x0008 | 在符号位或基数指示符后填入字符 | O |
ios::dec* | 0x0010 | 转换为十进制基数形式 | I/O |
ios::oct | 0x0020 | 转换为八进制基数形式 | I/O |
ios::hex | 0x0040 | 转换为十六进制基数形式 | I/O |
ios::showbase | 0x0080 | 在输出中显示基数指示符 | O |
ios::showpoint | 0x0100 | 在输出浮点数时必须带小数点和尾部的0
|
O |
ios::uppercase | 0x0200 | 以大写字母表示十六进制数,科学计数法使用大写字母 | O |
ios::showpos | 0x0400 | 正数前面加+ 号 |
O |
ios::scientific | 0x0800 | 科学计数法显示浮点数 | O |
#include <iostream>
#include <iomanip>
using namespace std;
int main() {
double x = 12.34;
cout << "1)" << setiosflags(ios::scientific | ios::showpos) << x << endl;
cout << "2)" << setiosflags(ios::fixed) << x << endl;
cout << "3)" << resetiosflags(ios::fixed) << setiosflags(ios::scientific | ios::showpos) << x << end;
cout << "4)" << resetiosflags(ios::showpos) << x << endl;
//1)+1.234000e+001
//2)+12.34
//3)+1.234000e+001
//4)1.234000e+001
return 0;
};
调用cout
的成员函数
成员函数 | 作用相同的流操作符 |
---|---|
precision(int np) | setprecision(np) |
width(int nw) | setw(nw) |
fill(char cFill) | setfill(cFill) |
setf(long iFlags) | setiosflags(iFlags) |
unsetf(long iFlags) | resetiosflags(iFlags) |
#include <iostream>
using namespace std;
int main(){
double values[] = {
1.23,
20.3456,
300.4567,
4000.45678,
50000.1234567
};
cout.fill('*');//设置填充字符为`*`
for (int i = 0; i < sizeof(values) / sizeof(double); i++) {
cout << "values[" << i << "] = (";
cout.width(10);//设置输出宽度
cout << values[i] << ")" << endl;
}
/*
values[0] = (******1.23)
values[1] = (***20.3456)
values[2] = (***300.457)
values[3] = (***4000.46)
values[4] = (***50000.1)
*/
cout.fill(' ');//设置填充字符为空格
for (int j = 0; j < sizefo(values) / sizeof(double); j++) {
cout << "values[" << j << "] = ("
cout.width(10);//设置输出宽度
cout.precision(j + 3);//设置保留有效数字
cout << values[j] << ")" << endl;
}
/*
values[0] = ( 1.23)
values[1] = ( 20.35)
values[2] = ( 300.46)
values[3] = ( 4000.46)
values[4] = ( 50000.12)
*/
return 0;
};
调用cin
的成员函数
istream
类提供了一些公有成员函数,它们可以以不同的方式提取输入流中的数据。
get()
函数
#include <iostream>
using namespace std;
int main() {
int n = 0;
char ch;
//当文件没有结束时继续进行循环
while ((ch = cin.get()) != EOF) {
cout.put(ch);
n++;
}
cout << "输入字符共计:" << n << endl;
};
在Windows
环境下,当进行键盘输入时,在单独的一行按ctrl + z
组合键后再按enter
键就代表文件输入结束。
getline()
函数
getline()
成员函数的原型如下:
istream & getline(char *buf, int bufSize);
其功能是从输入流中的当前字符开始读取bufSize - 1
个字符到缓冲区buf,或读到\n
为止(哪个条件先满足即按哪个执行)。函数会在buf
中读入数据的结尾自动添加串结束标记\0
。
istream & getline(char *buf, int bufSize, char delim);
其功能是从输入流中的当前字符开始读取bufSize - 1
个字符到缓冲区buf
,或读到字符delim
为止(哪个条件先满足即按哪个执行)。函数会在buf
中读入数据的结尾自动添加\0
。
两者的区别在于,前者是读到\n
为止,后者是读到指定字符delim
为止。字符\n
或delim
都不会被存入buf
中,但会从输入流中取走。
函数getline()
的返回值是函数所作用的对象的引用。如果输入流中\n
或delim
之前的字符个数达到或超过bufSize
,则会导致读入操作出错,其结果是:虽然本次读入已经完成,但是之后的读入都会失败。
#include <iostream>
using namespace std;
int main() {
char buf[10];
int i = 0;
//若输入流的一行超过9个字符,则会出错
while (cin.getline(buf, 10)) {
cout << ++i << ":" << buf << endl;
}
cout << "last:" << buf << endl;
return 0;
};
eof()
函数
eof()
成员函数的原型如下:
bool eof();
eof()
函数用于判断输入流是否已经结束。返回值为true
表示输入结束。
在应用程序中可以用eof()函数
测试是否到达文件尾,当文件操作结束遇到文件尾时,函数返回1
;否则返回0
。
ignore()
函数
ignore()
成员函数的原型如下:
istream & ignore(int n = 1, int delim = EOF);
此函数的作用是跳过输入流中的n
个字符,或跳过delim
及其之前的所有字符(哪个条件先满足就按哪个执行)。两个参数都有默认值。因此cin.ignore()
等效于cin.ignore(1, EOF)
,即跳过一个字符。该函数常用于跳过输入中的无用部分,以便提取有用的部分。
#include <iostream>
using namespace std;
int main() {
char str[30];
while (!cin.eof()) {
cin.ignore(10, ':');
if (!cin.eof()) {
cin >> str;
cout << str << endl;
}
}
return 0;
};
peek()
函数
peek()
成员函数的原型如下:
int peek();
函数peek()
返回输入流中的当前字符,但是并不将该字符从输入流中取走,相当于只是“看了一眼”将要读入的下一个字符,因此叫“窥视”。
cin.peek()
不会跳过输入流中的空格和回车符。在输入流已经结束的情况下,cin.peek()
返回EOF
。