从本章节开始进入第二部分:C++标准库!
(开始艰难起来了orz)
IO类
(举例:包含iostream
头文件后还需要写using std::cin;
)
(要是图省事就用using namespace std;
)
C++标准IO库:
- 包含
istream
、ostream
、iostream
类型 - 包含
ifstream
、ofstream
、fstream
类型 - 包含
istringstream
、ostringstream
、stringstream
类型
注:每个类型还有对应的宽字符版本(定义在同一头文件中,如ifstream
对应wifstream
类型)
类型ifstream
和istringstream
都**继承**自istream
,因此,我们可以像使用istream
一样使用ifstream
和istringstream
(如像cin
一样用<<
操作这些类型的对象)。ostream
也是类似的。
- IO对象不能拷贝或赋值。因此,进行IO操作的函数通常用引用方式传递和返回流。读写一个IO对象会改变其状态,所以传递和返回的引用**不能是
const
**的。
管理流的状态
-
一个流一旦发生错误,其上后续的操作都会失败。因此,代码在使用一个流之前应该检查它是否处于良好状态。最简单的方法是把流对象当作一个条件来使用:
while (cin >> word) {...}
-
上述方式相当于使用
!cin.fail()
当作条件。除此以外,bad()
表示系统级错误,eof()
表示到达文件末尾。 -
流对象的
rdstate
成员返回一个iostate
值,对应当前流的状态。IO库定义了四个iostate
的constexpr
值:badbit
、eofbit
、failbit
、goodbit
。 -
clear
不接受参数的版本清除所有错误位。接受参数的版本接收一个iostate
值,表示新的流状态:cin.clear(); //使cin有效 //复位cin的failbit位,其他位保持不变 cin.clear(cin.rdstate() & ~cin.failbit);
管理输出缓冲
输出时,输出内容可能立即被打印出来,也可能被保存在缓冲区中,随后再打印。
导致缓冲刷新的原因:
-
程序正常结束
-
缓冲区满,需要刷新缓冲,后面的数据才会被写入
-
使用如
endl
、flush
、ends
的操纵符显式刷新缓冲区cout << "Yo!" << endl; //输出Yo和一个换行,然后刷新 cout << "Yo!" << ends; //输出Yo和一个空字符,然后刷新 cout << "Yo!" << flush; //输出Yo,然后刷新
-
用
unitbuf
设置流的内部状态。cout << unitbuf; //之后的所有输出操作后都会立即刷新 cout << nounitbuf; //回到正常的缓冲方式
注:
cerr
默认下是设置unitbuf
的。 -
一个输出流可能被关联到另一个流。当读写后者时,前者会被刷新。如,默认情况下,
cin
和cerr
都关联到cout
,因此读cin
或写cerr
都会导致cout
的缓冲区被刷新。交互式系统应关联输入、输出流,使得所有输出都会在用户输入信息前被打印出来。
//把cin关联到cerr,之后读cin将刷新cerr cin.tie(&cerr); //cin.tie()返回cin关联的输出流的指针
一个流最多被关联到一个流,但多个流可以同时被关联到
ostream
。
警告:如果程序崩溃,输出缓冲区可能不会被刷新,所输出的数据可能还停留在缓冲区等待打印!这就是为什么我们要在调试输出的语句后面加上endl!
文件输入和输出
头文件fstream
定义了三个类型来支持文件IO:ifstream
读数据,ofstream
写数据,fstream
读写数据。
使用文件流对象
-
利用IO类的继承关系,我们可以使用多态。如:在调用有
&ostream
参数的函数时,我们可以传递给它一个ofstream
对象。 -
打开文件有两种方式:
//方式一:infile可以是string,也可以是C风格字符数组 ifstream in(ifile); //方式二: ofstream out; out.open(ifile" + ".copy"); //检查open是否成功 if (out) {...}
如果调用
open
失败,failbit
会被置位。进行打开文件是否成功的检测是一个好习惯。 -
为了将流关联到另一个文件,必须首先关闭已关联的文件。
in.close(); //关闭文件 in.open(ifile + "2"); //打开另一个文件
-
当一个文件流对象被销毁(离开其作用域)时,与之关联的文件会被自动关闭,即
close
会被自动调用!(例如,在循环里定义一个ifstream
对象,则每次循环都会被创建和自动销毁)
文件模式
把命令行控制台当成是文件。
in
:读方式(不能用于ofstream
对象)out
:写方式,默认trunc
模式(不能用于ifstream
对象)trunc
:清除文件内容(前提是设置了out
)app
:写操作前定位到文件末尾(设置了app
也就隐式设置了out
)ate
:打开文件后定位到文件末尾(可用于任何对象)binary
:用二进制方式进行IO(可用于任何对象)
默认情况下,ifstream
使用in模式,ofstream
使用out
模式,fstream
使用in
和out
模式。
ofstream out1(fileName); //清除文件内容
ofstream out2(fileName, ofstream::app); //保留文件内容
ofstream out3;
out3.open(fileName, ofstream::app);
string流
把字符串看作是文件。
头文件sstream
定义了三个类型来支持内存IO:istringstream
从string
读数据,ostringstream
向string
写数据,stringstream
则支持双向操作。
特有操作:
sstream strm
和sstream strm(s)
:构造对象(s是字符串)strm.str()
:返回strm中所保存的string
拷贝strm.str(s)
:将string s
拷贝到strm中,无返回值
当我们的工作是对整行进行处理,并包括处理行内的单个单词时,通常可以使用istringstream
。
struct PersonInfo {
string name;
vector<string> phones;
};
//将控制台输入的姓名和电话号码(可能一个人有多个)信息保存到结构体数组中
string line, word;
vector<PersonInfo> phoneList;
while (getline(cin,line)) {
PersonInfo temp;
istringstream record(line); //绑定到刚读入的行
record >> temp.name;
while (record >> tempPhone)
temp.phones.push_back(tempPhone);
phoneList.push_back(temp);
}
相当于把一行string
信息作为控制台信息进行读写。