文章目录
C++primer-学习心得-<第二部分>-第八章-IO库
终于进入到这本书的第二部分-标准库,第二部分一共有五个章节:
- IO库
- 顺序容器
- 泛型算法
- 关联容器
- 动态内存
8. Io库
8.1 IO类
为了支持不同种类的IO处理操作,在istream和ostream之外,标准库还定义了其他一些IO类型。如下表列出:
头文件 | 类型 |
---|---|
iostream | istream,wistream 从流读取数据 |
ostream,wostream 向流写入数据 | |
iostream wiostream 读写流 | |
fstream | ifstream,wifstream 从文件读取数据 |
ofstream,wofsteam 向文件写入数据 | |
fstream,wfstream 读写文件 | |
sstream | istringstream,wistringstream 从string 读取数据 |
ostringstream ,wostringstream 向string 写入数据 | |
stringstream,wstringstream 读写string |
为支持宽字符的语言,标准库定义了一组类型和对象来操作wchar_t类型的数据。宽字符版本的类型和函数的名字以w开始,如wcin、wcout和wcerr。
通过继承机制,标准库是我们能忽略不同类型的流之间的差异。
1.IO对象无法拷贝或赋值
前面我们有提到cin,cout不能拷贝。IO对象不能拷贝和赋值也不能理解。
2. 条件状态
IO操作时常会伴随着错误。下表列出了一些IO类定义的一些函数和标志,可以帮助我们访问和操作流的条件状态。
strm::iostate | strm是一种IO类型,iostate是一种机器相关的类型,提供表达条件状态的完整功能 |
---|---|
strm::badit | 指出流已崩溃 |
strm::failbit | 指出一个IO操作失败 |
strm::eofbit | 指出流到达了文件结束 |
strm::goodbit | 指出流未处于错误状态,此值保证为零 |
s.eof() | 若流s的eof置位,则返回true |
s.fail() | 若流s的failbit或badbit置位,则返回true |
s.good() | 若流s处于有效状态返回ture |
s.bad() | 若流s的badbit置位返回true |
s.clear() | 将流s中的所有的条件状态位复位,将流的状态设置位有效,返回true |
s.clear(flags) | 根据给定的flags标志位返回void |
s.setstate(flags) | 根据给定的flags标志位将流s中对应条件状态位置位。flags的类型为strm::iostate。返回void |
s.rdstate() | 返回流s的当前条件状态,返回值类型为 strm::iostate |
如习题8.1的标准答案为:
#include<iostream>
#include<string>
using namespace std;
istream& read(istream& is=cin) {
int v;
while (is >> v || !is.eof()) {
if (is.bad())
throw runtime_error("IO错误");
if (is.fail()) {
cerr << "数据错误" << endl;
is.clear();
is.ignore(100, '\n');
continue;
}
cout << v << endl;
}
is.clear();
return is;
}
int main() {
read();
}
3.管理输出缓冲
每个输出流都保存一个缓冲区,用来保存程序读写的数据。有了缓冲机制,系统可以将程序的多个输出操作组合成单一的系统级写操作。由于设备的写操作可能很耗时,允许系统将多个输出操作组合为单一的设备写操作可以带来很大的性能提升。
导致缓冲刷新的原因有很多:
- 程序正常结束,作为main函数return操作的一部分,缓冲刷新被执行
- 缓冲区满时,需要刷新缓冲,之后新的数据才能继续写入缓冲区
- 操纵符如endl来显示刷新缓冲区
- 每个输出操作后可以用操作符unitbuf设置流的内部状态来清空缓冲区。默认情况下对cerr是设置unitbuf的,因此写到cerr的内容都是立即刷新的
- 一个输出流可能被关联到另一个流。此时,当读写被关联的流时,关联到的流的缓冲区会被刷新。
操作符endl完成换行并刷新缓冲区。此外,flush刷新缓冲区但不输出任何额外字符,ends向缓冲区插入一个空字符并刷新缓冲区。使用unitbuf操纵符使接下来每次写操作之后都进行一次flush操作,而nounifbuf操作符则使其恢复正常的系统管理的缓冲区刷新机制。
tie有两个重载的版本:一个版本不带参数,返回指向输出流的指针。如果本对象当前关联到一个输出流则返回的就是这个流的指针。未关联到流则返回空指针。tie的第二个版本接受一个指向ostream的指针,将自己关联到此ostream,即x.tie(&o)将流x关联到输出流o。
8.2 文件输入输出
头文件fstream定义了三个类型来支持文件IO:ifstream从一个给定文件读取数据,ofstream向一个给定的文件写入数据,fstream可以读写文件。除了继承iostream类型的行为外,fstream中定义的类型还增加了一些新的成员来管理与流关联的文件。如下表
fstream fstrm | 创建一个未绑定的文件流。fstream是头文件fstream中定义的一个类型 |
---|---|
fstream fstrm(s) | 创建一个fstream,并打开名为s的文件。s可以是string或指向c风格字符串的指针 |
fstream fstrm(s,modo) | 按指定mode打开文件 |
fstrm.open(s) | 打开名为s 的文件并将文件与fstrm绑定,返回void |
fstrm.close() | 关闭fstrm绑定的文件,返回void |
fstrm.is_open() | 返回一个bool值,指出与fstrm关联的文件是否成功打开且尚未关闭 |
1.使用文件流对象
如下面程序读取一个文件到一个string的vector中,以每个单词为独立的string。最后输出vector中的内容
#include<iostream>
#include<string>
#include<fstream>
#include<vector>
using namespace std;
void read() {
ifstream in("mAIN.cpp");
if (in.is_open()) {
vector<string> file_text;
string str;
while (in >> str) {
string strs = "";
for (char cc : str) {
if (isalpha(cc)) {
strs += cc;
}
else if (strs.size() >= 1) {
file_text.push_back(strs);
strs="";
}
}
}
for (auto c : file_text)
cout << c << endl;
}
in.close();
}
int main() {
read();
}
2. 文件模式
-
in 以读方式打开, 只可以对ifstream 或fstream
-
out 写,只可以对ofstream或fstream对象
-
app 每次写操作前定位到文件末尾,只要trunc没被设定就可以设定,在app模式下,即使没有显示指定out模式,文件也总是以输出方式打开
-
ate 打开文件后立即定位到文件末尾
-
trunc 截断文件,只有当out也被设定时才可设定,
-
binary 以二进制方式进行IO
-
ate和binary模式可用于任何类型的 文件流对象,且可以与其他任何文件模式组合使用
-
默认情况下,即使我们没有指定trunc,以out模式打开的文件也会被截断。为保留以out模式打开的文件到内容,我们必须同时指定app模式;或者同时指定in模式,打开文件的同时执行读写操作。
每个文件流类型都定义了默认的文件模式,如ifstream关联的文件默认以in打开,ofstream默认以out打开,fstream默认以in和out打开。
注意以out模式打开的文件会丢弃已有数据。
每次调用open时都会确定文件模式,打开文件时可以改变文件模式。
如下是一个简单的从键盘读取输入的字符串写入到文件中,最后读取写入的文件并打印出来
#include<iostream>
#include<fstream>
#include<string>
using namespace std;
void write(string file) {
string str;
ofstream out;
out.open(file, ofstream::app);
if (out.is_open()) {
while(cin >> str) {
out << str << endl;
}
}
out.close();
}
void read(string file) {
ifstream in(file);
if (in.is_open()) {
string str;
while (in >> str) {
cout << str << endl;
}
}
else {
cerr << "error" << endl;
}
in.close();
}
int main() {
string file = "test.txt";
write(file);
read(file);
}
8.3 string流
sstrea头文件定义了三个类型来支持内存IO,这些内存可以向string写入数据,从string读取数据。
stringstream特有的操作
- sstream strm
- sstream strm(s) strm保存string s的拷贝
- strm.str() 返回strm保存的拷贝
- strm.str(s) 将string s拷贝到strm中,返回void
1. 使用istringstream
如下面代码完成练习8.9
#include<iostream>
#include<string>
#include<sstream>
using namespace std;
istream& read(istream& is = cin) {
int v;
while (is >> v || !is.eof()) {
if (is.bad())
throw runtime_error("IO错误");
if (is.fail()) {
cerr << "数据错误" << endl;
is.clear();
is.ignore(100, '\n');
continue;
}
cout << v << endl;
}
is.clear();
return is;
}
int main() {
istringstream ss(string("123"));
read(ss);
}
练习8.10
#include<iostream>
#include<string>
#include<sstream>
#include<fstream>
#include<vector>
using namespace std;
int main() {
ifstream in("main.cpp",ifstream::binary);
vector<string>filetext;
vector<istringstream> ist;
string strs;
if (in.is_open()) {
while (in >> strs) {
filetext.push_back(strs);
}
}
in.close();
for (auto c : filetext) {
string str;
for (auto cc : c) {
if (isalpha(cc)) {
str += cc;
}
else if(str.size()>=1){
istringstream s(str);
//ist.push_back(s);
cout << s.str() << endl;
str = "";
}
}
}
}
练习8.11
#include<iostream>
#include<string>
#include<sstream>
#include<fstream>
#include<vector>
using namespace std;
struct PersonInfo {
string name;
vector<string> phones;
};
int main() {
string line, word;
vector<PersonInfo> people;
istringstream record;
while (getline(cin, line)) {
PersonInfo info;
record.str(line);
record >> info.name;
while (record >> word)
info.phones.push_back(word);
}
}
2. 使用ostringstream
#include<iostream>
#include<string>
#include<sstream>
#include<fstream>
#include<vector>
using namespace std;
struct PersonInfo {
string name;
vector<string> phones;
};
int main() {
string line, word;
vector<PersonInfo> people;
//istringstream record;
ostringstream name;
while (getline(cin, line)) {
PersonInfo info;
istringstream record(line);
record >> info.name;
cout << "????"<< endl;
string strs;
while (record>>strs) {
ostringstream badnum, formatted, test;
test<<strs;
for (auto a : test.str()) {
if (!isdigit(a)) {
badnum <<" "<< test.str();
cerr << "invalid input" << endl;
break;
}
else if (a == test.str()[test.str().size() - 1]) {
formatted <<" "<< test.str();
info.phones.push_back(formatted.str());
}
}
}
people.push_back(info);
cout << "number: "<<people.size() <<" "<<info.name<< endl;
}
for (auto c : people) {
cout << c.name << endl;
for (auto cc : c.phones) {
cout << "phones: " << cc << endl;
}
}
}