在C++中,数据I/O通过流来实现
输出流:针对数据写入目标的流
输入流:针对数据读取来源的流
C++的流(stream)源自ANSI C中的标准I/O流,它与UNIX系统V的STREAMS I/O系统是两个完全不同的概念
istream及其子类支持输入操作
ostream及其子类支持输出操作
由istream和ostream类共同派生的子类iostream既支持输入操作,也支持输出操作
xxxxxxxxxx
541// text.cpp
2
3// 文本文件的格式化I/O
4
5
6
7
8using namespace std;
9
10int main(void) {
11 // 如果参数文件名所代表的文件并不存在,那么程序将按照缺省行为创建这
12 // 个文件。如果这个文件原先已经存在,它的内容将从开始位置全部被覆盖
13 ofstream ofs("text.txt");
14 if (!ofs) {
15 cout << "Unable to open file for writing" << endl;
16 return -1;
17 }
18
19 ofs << 1234 << ' ' << 56.78 << ' ' << "apples" << '\n';
20
21 ofs.close();
22
23 ofs.open("text.txt", ios::app); // 把数据追加到原有内容的后面
24 if (!ofs) {
25 cout << "Unable to open file for appending" << endl;
26 return -1;
27 }
28
29 ofs << "append_a_line\n";
30
31 ofs.close();
32
33 ifstream ifs("text.txt"); // 要求文件必须存在
34 if (!ifs) {
35 cout << "Unable to open file for reading" << endl;
36 return -1;
37 }
38
39 int n;
40 double d;
41 string str1, str2;
42
43 // 经重载定义的提取操作符“>>”能够按照预期的数据类型进行
44 // 格式化提取操作。缺省情况下,提取操作符会自动跳过输入
45 // 文件中各个数据项间的空白字符(空格、制表符、换行符等)
46
47 ifs >> n >> d >> str1 >> str2;
48
49 cout << n << ' ' << d << ' ' << str1 << ' ' << str2 << endl;
50
51 ifs.close();
52
53 return 0;
54}
xxxxxxxxxx
31ifstream(const char* filename, openmode mode);
2ofstream(const char* filename, openmode mode);
3fstream(const char* filename, openmode mode);
xxxxxxxxxx
21void open(const char* filename, openmode mode = default_mode);
2void close(void);
ios::in
打开文件用于读取,不存在则失败,存在不清空
适用于ifstream(缺省)和fstream
ios::out
打开文件用于写入,不存在则创建,存在则清空
适用于ofstream(缺省)和fstream
ios::app
打开文件用于追加,不存在则创建,存在不清空
适用于ofstream和fstream
ios::trunc
打开时清空原内容
适用于ofstream和fstream
ios::ate
打开时定位文件尾
适用于ifstream、ofstream和fstream
ios::binary
以二进制模式读写
适用于ifstream、ofstream和fstream
打开模式可以组合使用,如:
ios::in|ios::out表示既读取又写入
打开模式不能随意组合,如:
ios::in|ios::trunc不合理,清空同时读取没有意义
ios::in|ios::out|ios::trunc合理,清空原内容,写入新内容,同时读取
I/O流对象中包含一个类型为ios::iostate的成员变量,表示I/O流的当前状态,可通过状态函数访问该状态
状态函数 | 说明 |
---|---|
bool ios::good(void); | 状态位全零即流可用返回true,否则返回false |
bool ios::bad(void); | badbit位为1返回true,否则返回false |
bool ios::eof(void); | eofbit位为1返回true,否则返回false |
bool ios::fail(void); | badbit或failbit位为1返回true,否则返回false |
iostate ios::rdstate(void); | 获取当前状态 |
void ios::clear(iostate s=ios::goodbit); | 设置(复位)状态 |
void ios::setstate(iostate s); | 添加状态 |
I/O流对象在任何时候都可被隐式转换为布尔值,只在状态位全零时,该布尔值为true,其它均为false
xxxxxxxxxx
61...
2if (!ofs) {
3 cout << "Unable to open file for writing" << endl;
4 return -1;
5}
6...
xxxxxxxxxx
461// position.cpp
2
3// 文件位置
4
5
6
7
8using namespace std;
9
10int main(void) {
11 // fstream类的缺省打开模式是ios::in|ios::out,
12 // 以该模式打开文件,要求文件必须存在
13 fstream fs("position.txt", ios::in|ios::out);
14 if (!fs) {
15 cout << "Unable to open file for reading and writing" << endl;
16 return -1;
17 }
18
19 fs << 1234 << ' ' << 56.78 << ' ' << "apples" << '\n';
20
21 // 在某些操作系统上,以文本方式写入流时,系统会将一个换行符自动
22 // 扩展为一个回车符加一个换行符的形式(可通过hexdump -C命令查看)
23
24 cout << fs.tellg() << endl; // 获得当前get位置
25 cout << fs.tellp() << endl; // 获得当前put位置
26
27 // get和put位置本应用独立的变量表示,其中任何一个都
28 // 可以指向文件的任何位置,但大多数实现并没有这么做
29
30 fs.seekg(ios::beg); // 设置get位置
31
32 cout << fs.tellg() << endl; // 在将get位置调整到文件开始的同时,
33 cout << fs.tellp() << endl; // put位置也随之被调整到了文件开始
34
35 int n;
36 double d;
37 string str;
38
39 fs >> n >> d >> str;
40
41 cout << n << ' ' << d << ' ' << str << endl;
42
43 fs.close ();
44
45 return 0;
46}
xxxxxxxxxx
21istream& istream::seekg(off_type offset, ios::seekdir origin);
2ostream& ostream::seekp(off_type offset, ios::seekdir origin);
origin表示偏移量offset的起点
ios::beg表示从文件头(文件的第一个字节)偏移offset字节
ios::cur表示从当前位置(最近一次被读写字节的下一个位置)偏移offset字节
ios::end表示从文件尾(文件的最后一个字节的下一个位置)偏移offset字节
offset表示偏移字节数
正数表示向文件尾方向偏移
负数表示向文件头方向偏移
读写指针不能被设置到文件头之前或文件尾之后,否则流状态异常
xxxxxxxxxx
391// seek.cpp
2
3// seek函数的两参数版本
4
5
6
7
8using namespace std;
9
10int main(void) {
11 fstream fs("seek.txt", ios::in|ios::out);
12 if (!fs) {
13 cout << "Unable to open file for reading and writing" << endl;
14 return -1;
15 }
16
17 fs << 1234 << ' ' << 56.78 << ' ' << "apples" << '\n';
18
19 cout << fs.tellg() << endl;
20 cout << fs.tellp() << endl;
21
22 fs.seekg(0); // seekg(0)等价于seekg(ios::beg),将get位置移至文件开始
23
24 cout << fs.tellg() << endl;
25 cout << fs.tellp() << endl;
26
27 int n;
28 string str;
29
30 fs >> n;
31 fs.seekg(6, ios::cur); // 从当前位置向文件尾方向偏移6个字节
32 fs >> str;
33
34 cout << n << ' ' << str << endl;
35
36 fs.close();
37
38 return 0;
39}
xxxxxxxxxx
51ostream& put(char ch);
2int get();
3istream& get(char& ch);
4istream& get(char* buffer, streamsize num, char delim);
5istream& getline(char* buffer, streamsize num, char delim);
xxxxxxxxxx
1111// putget.cpp
2
3// 非格式化I/O
4
5
6
7
8using namespace std;
9
10int main(void) {
11 // put函数
12
13 ofstream ofs("putget.txt");
14 if (!ofs) {
15 cout << "Unable to open file for writing" << endl;
16 return -1;
17 }
18
19 for (char c = ' '; c <= '~'; ++c)
20 // 将参数字符插入到其调用输出流中,并返回该输出流对象
21 if (!ofs.put(c)) {
22 cout << "Unable to write file" << endl;
23 return -1;
24 }
25
26 ofs.put('\n');
27
28 ofs.close();
29
30 // 无参get函数
31
32 ifstream ifs("putget.txt");
33 if (!ifs) {
34 cout << "Unable to open file for reading" << endl;
35 return -1;
36 }
37
38 char c;
39
40 while ((c = ifs.get()) != EOF)
41 cout << c;
42
43 if (!ifs.eof()) {
44 cout << "Unable to read file" << endl;
45 return -1;
46 }
47
48 // 单参get函数
49
50 ifs.clear();
51 ifs.seekg(ios::beg);
52
53 // 单参get函数返回调用该函数的流对象,当该流已
54 // 到文件尾或发生错误是返回false,否则返回true
55 while (ifs.get(c))
56 cout << c;
57
58 if (!ifs.eof()) {
59 cout << "Unable to read file" << endl;
60 return -1;
61 }
62
63 // 三参get函数
64
65 ifs.clear();
66 ifs.seekg(ios::beg);
67
68 char buf[4];
69
70 // 读取从当前位置开始直到作为定界符的第三个参数之间,最多不超过第二个
71 // 参数-1个字符,到第一个参数所指向的字符数组中,追加结尾空字符,并返
72 // 回调用流对象。缺省定界符是换行符(\n)。每调用一次get函数,定界符
73 // 并不被提取,读写指针停在定界符处,这时可通过ignore函数,跳过定界符
74 while (ifs.get(buf, sizeof(buf), '\n')) {
75 cout << buf;
76
77 // 当get函数试图从定界符上读取内容时,将发生错误
78 if (ifs.peek() == '\n')
79 ifs.ignore();
80 }
81
82 cout << endl;
83
84 if (!ifs.eof()) {
85 cout << "Unable to read file" << endl;
86 return -1;
87 }
88
89 // getline函数
90
91 ifs.clear();
92 ifs.seekg(0, ios::beg);
93
94 char line[128];
95
96 // 当getline函数试图读取多于第二个参数-1个字符时,将发生错误
97 // getline函数会可以自动跳过定界符,无需通过ignore函数忽略之
98 while (ifs.getline(line, sizeof(line), '\n'))
99 cout << line;
100
101 cout << endl;
102
103 if (!ifs.eof()) {
104 cout << "Unable to read file" << endl;
105 return -1;
106 }
107
108 ifs.close();
109
110 return 0;
111}
xxxxxxxxxx
21istream& read(char* buffer, streamsize num);
2ostream& write(const char* buffer, streamsize num);
xxxxxxxxxx
461// binary.cpp
2
3// 分块读写
4
5
6
7
8using namespace std;
9
10int main(int argc, char* argv[]) {
11 if (argc < 3) {
12 cout << "Usage: " << argv[0] << " <input_file> <output_file>" << endl;
13 return -1;
14 }
15
16 ifstream ifs(argv[1], ios::binary);
17 if (!ifs) {
18 cout << "Unable to open file for reading" << endl;
19 return -1;
20 }
21
22 ofstream ofs(argv[2], ios::binary);
23 if (!ofs) {
24 cout << "Unable to open file for writing" << endl;
25 return -1;
26 }
27
28 char buf[32];
29
30 // read函数返回调用流对象,读至文件尾或出错时返回false
31 while (ifs.read(buf, sizeof(buf)))
32 ofs.write(buf, sizeof(buf));
33
34 if (!ifs.eof()) {
35 cout << "Unable to read file" << endl;
36 return -1;
37 }
38
39 // 通过gcount函数获取最后一次读取到的字节数
40 ofs.write(buf, ifs.gcount());
41
42 ofs.close();
43 ifs.close();
44
45 return 0;
46}
xxxxxxxxxx
531// binentire.cpp
2
3// 完整读写
4
5
6
7
8using namespace std;
9
10int main(int argc, char* argv[]) {
11 if (argc < 3) {
12 cout << "Usage: " << argv[0] << " <input_file> <output_file>" << endl;
13 return -1;
14 }
15
16 // 在打开文件流时使用ios::ate选项,将文件的get位置置于文件尾
17 ifstream ifs(argv[1], ios::binary|ios::ate);
18 if (!ifs) {
19 cout << "Unable to open file for reading" << endl;
20 return -1;
21 }
22
23 size_t bytes = ifs.tellg(); // 通过tellg函数获取当前get位置,即文件总字节数
24 ifs.seekg(ios::beg); // 将get位置重置于文件头
25
26 char* buf = new char[bytes]; // 分配足量缓冲区
27
28 ifs.read(buf, bytes); // 一次读取文件全部内容
29 if (!ifs) {
30 cout << "Unable to read file" << endl;
31 return -1;
32 }
33
34 ifs.close();
35
36 ofstream ofs(argv[2], ios::binary);
37 if (!ofs) {
38 cout << "Unable to open file for writing" << endl;
39 return -1;
40 }
41
42 ofs.write(buf, bytes);
43 if (!ofs) {
44 cout << "Unable to write file" << endl;
45 return -1;
46 }
47
48 ofs.close();
49
50 delete[] buf;
51
52 return 0;
53}
向文件写入多字节整数,按内存地址从低到高的顺序,依次写入每一个字节:
xxxxxxxxxx
281// byteorder.cpp
2
3// 从低地址到高地址写文件
4
5
6
7
8using namespace std;
9
10int main(void) {
11 ofstream ofs ("byteorder.bin", ios::binary);
12 if (!ofs) {
13 cout << "Unable to open file for writing" << endl;
14 return -1;
15 }
16
17 int n = 0x12345678;
18
19 ofs.write((char*)&n, sizeof(n));
20 if (!ofs) {
21 cout << "Unable to write file" << endl;
22 return -1;
23 }
24
25 ofs.close();
26
27 return 0;
28}
xxxxxxxxxx
531// array.cpp
2
3// 数组读写
4
5
6
7
8using namespace std;
9
10int main(void) {
11 ofstream ofs("array.bin", ios::binary);
12 if (!ofs) {
13 cout << "Unable to open file for writing" << endl;
14 return -1;
15 }
16
17 int oarr[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
18 ofs.write((char*)oarr, sizeof(oarr));
19 if (!ofs) {
20 cout << "Unable to write file" << endl;
21 return -1;
22 }
23
24 ofs.close();
25
26 ifstream ifs("array.bin", ios::binary|ios::ate);
27 if (!ifs) {
28 cout << "Unable to open file for reading" << endl;
29 return -1;
30 }
31
32 size_t bytes = ifs.tellg();
33 ifs.seekg(ios::beg);
34
35 size_t num = bytes/sizeof(int);
36 int* iarr = new int[num];
37
38 ifs.read ((char*)iarr, bytes);
39 if (!ifs) {
40 cout << "Unable to read file" << endl;
41 return -1;
42 }
43
44 ifs.close();
45
46 for (size_t i = 0; i < num; ++i)
47 cout << iarr[i] << ' ';
48 cout << endl;
49
50 delete[] iarr;
51
52 return 0;
53}
流函数 | 功能 |
---|---|
int ios::precision(int); | 设置浮点精度,返回原精度 |
int ios::precision(void) const; | 获取浮点精度 |
int ios::width(int); | 设置显示域宽,返回原域宽 |
int ios::width(void) const; | 获取显示域宽 |
char ios::fill(char); | 设置填充字符,返回原字符 |
char ios::fill(void) const; | 获取填充字符 |
long ios::flags(long); | 设置格式标志,返回原标志 |
long ios::flags(void) const; | 获取格式标志 |
long ios::setf(long); | 添加格式标志位,返回原标志 |
long ios::setf(long, long); | 添加格式标志位,返回原标志 先用第二个参数将互斥域清零 |
long ios::unsetf(long); | 清除格式标志位,返回原标志 |
般而言,对I/O流格式的改变都是持久的,即只要不再设置新格式,当前格式将始终保持下去
显示域宽是个例外,通过ios::width(int)
所设置的显示域宽,只影响紧随其后的第一次输出,再往后的输出又恢复到默认状态
格式标志 | 互斥域 | 效果 |
---|---|---|
ios::left | ios::adjustfield | 左对齐 |
ios::right | ios::adjustfield | 右对齐 |
ios::internal | ios::adjustfield | 数值右对齐,符号左对齐 |
ios::dec | ios::basefield | 十进制 |
ios::oct | ios::basefield | 八进制 |
ios::hex | ios::basefield | 十六进制 |
ios::fixed | ios::floatfield | 用定点小数表示浮点数 |
ios::scientific | ios::floatfield | 用科学计数法表示浮点数 |
ios::showpos | — | 正整数前面显示+号 |
ios::showbase | — | 显示进制前缀0或0x |
ios::showpoint | — | 显示小数点和尾数0 |
ios::uppercase | — | 数中字母显示为大写 |
ios::boolalpha | — | 用字符串表示布尔值 |
ios::unitbuf | — | 每次插入都刷流缓冲 |
ios::skipws | — | 以空白字符作分隔符 |
流控制符 | 功能 | 输入 | 输出 |
---|---|---|---|
left | 左对齐 | ||
right | 右对齐 | ||
internal | 数值右对齐,符号左对齐 | ||
dec | 十进制 | ||
oct | 八进制 | ||
hex | 十六进制 | ||
fixed | 用定点小数表示浮点数 | ||
scientific | 用科学计数法表示浮点数 | ||
(no)showpos | 正整数前面(不)显示+号 | ||
(no)showbase | (不)显示进制前缀0或0x | ||
(no)showpoint | (不)显示小数点和尾数0 | ||
(no)uppercase | 数中字母(不)显示为大写 | ||
(no)boolalpha | (不)用字符串表示布尔值 | ||
(no)unitbuf | (不)每次插入都刷流缓冲 | ||
(no)skipws | (不)以空白字符作分隔符 | ||
ws | 跳过前导空白字符 | ||
ends | 空字符 | ||
endl | 换行符,刷流缓冲 | ||
flush | 刷流缓冲 | ||
setprecision(int) | 设置浮点精度 | ||
setw(int) | 设置显示域宽 | ||
setfill(int) | 设置填充字符 | ||
setiosflags(long) | 设置格式标志 | ||
resetiosflags(long) | 清除格式标志 |
xxxxxxxxxx
1491// format.cpp
2
3// 格式控制
4
5
6
7
8
9// 带参数的流控制符
10
11
12using namespace std;
13
14int main(void) {
15 // 流函数
16 cout.precision(10);
17 cout << cout.precision () << endl; // 无参precision函数,返回当前精度
18 cout << sqrt(200) << endl;
19 cout.setf(ios::scientific);
20 cout << sqrt(200) << endl;
21 cout.unsetf(ios::scientific);
22
23 // 流控制符
24 cout << setprecision(5) << sqrt(200) << ' ' <<
25 scientific << sqrt(200) << resetiosflags(ios::scientific) << endl;
26
27 // 四舍五入而非简单截断
28 cout << setprecision(2) << 1.24 << ' ' << 1.25 << ' ' << 1.26 << endl;
29
30 // 大多数用于格式化的流函数和流控制符对流的影响会一直持续到下一次对该格式的改变
31 cout.precision(5);
32 cout << 1.23456 << endl;
33 cout << 1.234567 << endl;
34 cout << setprecision(8) << 1.234567 << endl;
35
36 // 以十进制、八进制和十六进制表示一个整数
37 cout << oct << 127 << endl;
38 cout << hex << 127 << endl;
39 cout << dec << 127 << endl;
40
41 // 流控制符setw仅对其下一个输出的域宽进行控制,不足域宽部分以填充字符填充
42 cout << setw(7) << oct << 127 << endl;
43 cout << setw(7) << hex << 127 << endl;
44 cout << setw(7) << dec << 127 << endl;
45
46 // 缺省填充字符是空格,但可以通过流函数fill或流控制符setfill指定
47 cout << setfill('#') << setw(7) << oct << 127 << endl;
48 cout << setfill('@') << setw(7) << hex << 127 << endl;
49 cout << setfill('%') << setw(7) << dec << 127 << endl;
50 cout.fill('$');
51 cout << setw(7) << oct << 127 << endl;
52 cout.fill('&');
53 cout << setw(7) << hex << 127 << endl;
54 cout.fill('*');
55 cout << setw(7) << dec << 127 << endl;
56
57 // 指定对齐方式
58 cout << left << setfill('#') << setw(7) << oct << 127 << endl;
59 cout << setfill('@') << setw(7) << hex << 127 << endl;
60 cout << setfill('%') << setw(7) << dec << 127 << endl;
61 cout.setf(ios::right);
62 cout.fill('$');
63 cout << setw(7) << oct << 127 << endl;
64 cout.fill('&');
65 cout << setw(7) << hex << 127 << endl;
66 cout.fill('*');
67 cout << setw(7) << dec << 127 << endl;
68
69 // 必须为每个输出独立设置域宽,即使它们的域宽相同
70 cout << setfill('#') << setw(7) << oct << 127 << hex << 127 << dec << 127 << endl;
71 cout << setw(7) << oct << 127 << setw(7) << hex << 127 << setw(7) << dec << 127 << endl;
72
73 // 显示进制前缀
74 cout << showbase << oct << 127 << endl;
75 cout << hex << 127 << endl;
76 cout << dec << 127 << endl;
77 cout.setf(ios::showbase);
78 cout << oct << 127 << endl;
79 cout << hex << 127 << endl;
80 cout << dec << 127 << endl;
81
82 // 不显示进制前缀
83 cout << noshowbase << oct << 127 << endl;
84 cout << hex << 127 << endl;
85 cout << dec << 127 << endl;
86 cout.unsetf(ios::showbase);
87 cout << oct << 127 << endl;
88 cout << hex << 127 << endl;
89 cout << dec << 127 << endl;
90
91 // 显示小数点
92 cout << 12.00 << endl;
93 cout << setprecision(10) << 12.00 << endl;
94 cout << showpoint << 12.00 << endl;
95 cout.setf(ios::showpoint);
96 cout << 12.00 << endl;
97
98 // 不显示小数点
99 cout << noshowpoint << 12.00 << endl;
100 cout.unsetf(ios::showpoint);
101 cout << 12.00 << endl;
102
103 // 科学计数法和定点小数
104 cout.precision(4);
105 cout << sqrt(200) << endl; // 对于定点小数法,精度限制整个浮点数的显示位数
106 cout.setf(ios::scientific);
107 cout << sqrt(200) << endl; // 对于科学计数法,精度限制底数小数的显示位数
108 cout.precision(8);
109 cout << sqrt(200) << endl;
110 cout.unsetf(ios::scientific);
111 cout << sqrt(200) << endl;
112
113 // 流控制符noskipws或流函数unsetf(ios::skipws)用于取消跳过空白
114 // 流控制符skipws或流函数setf(ios::skipws)用于启用跳过空白
115 ifstream ifs("format.txt");
116 if (!ifs) {
117 cout << "Unable to open file for reading" << endl;
118 return -1;
119 }
120
121 ifs.unsetf(ios::skipws);
122
123 char c;
124
125 while (ifs >> c)
126 cout << c;
127
128 cout << endl << "---------" << endl;
129
130 ifs.clear();
131 ifs.seekg(ios::beg);
132
133 ifs.setf(ios::skipws);
134
135 while (ifs >> c)
136 cout << c;
137
138 cout << endl << "---------" << endl;
139
140 ifs.close();
141
142 cout.width(10);
143 cout.fill('-');
144 cout.setf(ios::internal, ios::adjustfield);
145 cout.setf(ios::showpos);
146 cout << 12345 << endl;
147
148 return 0;
149}
xxxxxxxxxx
371// sstream.cpp
2
3// 字符串流
4
5
6
7
8using namespace std;
9
10int main(void) {
11 // ostringstream对象的一个优点是,可以利用插入操作符(<<)
12 // 的格式化能力把数值类型的数据转换为它们的字符串表示形式
13
14 ostringstream oss;
15 oss << 1234 << ' ' << 56.78 << ' ' << "apples";
16
17 // 通过字符串流的str成员函数,访问与之相关联的字符串对象
18
19 cout << oss.str() << endl;
20
21 // istringstream对象的一个优点是,可以利用提取操作符(>>)
22 // 的格式化能力从字符串中解析出不同类型的数值
23
24 // istringstream iss(oss.str());
25 istringstream iss;
26 iss.str(oss.str());
27
28 int n;
29 double d;
30 string str;
31
32 iss >> n >> d >> str;
33
34 cout << n << ' ' << d << ' ' << str << endl;
35
36 return 0;
37}
缺省情况下,所有C++流都是带缓冲的。与一个流相关联的缓冲区是streambuf类型的对象。缓冲意味着当向一个输出文件流写入数据时,所输出的字节在一般情况下并不会立即被写到文件中。相反,它们会被暂时转移到一块作为输出流缓冲区的内存块中。如有以下情况之一发生:
缓冲区被填满
流被关闭
显式调用该流的flush成员函数或向该流插入endl控制符
调用该流的sync成员函数以建立同步
流缓冲区立即被刷新,其中所有的数据全部转移到输出文件中。