【C++Primer笔记】第八章 IO库

从本章节开始进入第二部分:C++标准库!

(开始艰难起来了orz)

文章目录

    • IO类
      • 管理流的状态
      • 管理输出缓冲
    • 文件输入和输出
      • 使用文件流对象
      • 文件模式
    • string流

IO类

(举例:包含iostream头文件后还需要写using std::cin;

(要是图省事就用using namespace std;

C++标准IO库:

  • 包含istreamostreamiostream类型
  • 包含ifstreamofstreamfstream类型
  • 包含istringstreamostringstreamstringstream类型

注:每个类型还有对应的宽字符版本(定义在同一头文件中,如ifstream对应wifstream类型)

类型ifstreamistringstream都**继承**自istream,因此,我们可以像使用istream一样使用ifstreamistringstream(如像cin一样用<<操作这些类型的对象)。ostream也是类似的。

  • IO对象不能拷贝或赋值。因此,进行IO操作的函数通常用引用方式传递和返回流。读写一个IO对象会改变其状态,所以传递和返回的引用**不能是const**的。

管理流的状态

  1. 一个流一旦发生错误,其上后续的操作都会失败。因此,代码在使用一个流之前应该检查它是否处于良好状态。最简单的方法是把流对象当作一个条件来使用:

    while (cin >> word) {...}
    
  2. 上述方式相当于使用!cin.fail()当作条件。除此以外,bad()表示系统级错误,eof()表示到达文件末尾。

  3. 流对象的rdstate成员返回一个iostate值,对应当前流的状态。IO库定义了四个iostateconstexpr值:badbiteofbitfailbitgoodbit

  4. clear不接受参数的版本清除所有错误位。接受参数的版本接收一个iostate值,表示新的流状态:

    cin.clear(); //使cin有效
    //复位cin的failbit位,其他位保持不变
    cin.clear(cin.rdstate() & ~cin.failbit);
    

管理输出缓冲

输出时,输出内容可能立即被打印出来,也可能被保存在缓冲区中,随后再打印。

导致缓冲刷新的原因:

  • 程序正常结束

  • 缓冲区满,需要刷新缓冲,后面的数据才会被写入

  • 使用如endlflushends的操纵符显式刷新缓冲区

    cout << "Yo!" << endl; //输出Yo和一个换行,然后刷新
    cout << "Yo!" << ends; //输出Yo和一个空字符,然后刷新
    cout << "Yo!" << flush; //输出Yo,然后刷新
    
  • unitbuf设置流的内部状态。

    cout << unitbuf; //之后的所有输出操作后都会立即刷新
    cout << nounitbuf; //回到正常的缓冲方式
    

    注:cerr默认下是设置unitbuf的。

  • 一个输出流可能被关联到另一个流。当读写后者时,前者会被刷新。如,默认情况下,cincerr都关联到cout,因此读cin或写cerr都会导致cout的缓冲区被刷新。

    交互式系统应关联输入、输出流,使得所有输出都会在用户输入信息前被打印出来。

    //把cin关联到cerr,之后读cin将刷新cerr
    cin.tie(&cerr); 
    //cin.tie()返回cin关联的输出流的指针
    

    一个流最多被关联到一个流,但多个流可以同时被关联到ostream

警告:如果程序崩溃,输出缓冲区可能不会被刷新,所输出的数据可能还停留在缓冲区等待打印!这就是为什么我们要在调试输出的语句后面加上endl!

文件输入和输出

头文件fstream定义了三个类型来支持文件IO:ifstream读数据,ofstream写数据,fstream读写数据。

使用文件流对象

  1. 利用IO类的继承关系,我们可以使用多态。如:在调用有&ostream参数的函数时,我们可以传递给它一个ofstream对象。

  2. 打开文件有两种方式:

    //方式一:infile可以是string,也可以是C风格字符数组
    ifstream in(ifile);
    //方式二:
    ofstream out;
    out.open(ifile" + ".copy");
    //检查open是否成功
    if (out) {...}
    

    如果调用open失败,failbit会被置位。进行打开文件是否成功的检测是一个好习惯。

  3. 为了将流关联到另一个文件,必须首先关闭已关联的文件

    in.close(); //关闭文件
    in.open(ifile + "2"); //打开另一个文件
    
  4. 当一个文件流对象被销毁(离开其作用域)时,与之关联的文件会被自动关闭,即close会被自动调用!(例如,在循环里定义一个ifstream对象,则每次循环都会被创建和自动销毁)

文件模式

把命令行控制台当成是文件。

  • in:读方式(不能用于ofstream对象)
  • out:写方式,默认trunc模式(不能用于ifstream对象)
  • trunc:清除文件内容(前提是设置了out
  • app:写操作前定位到文件末尾(设置了app也就隐式设置了out
  • ate:打开文件后定位到文件末尾(可用于任何对象)
  • binary:用二进制方式进行IO(可用于任何对象)

默认情况下,ifstream使用in模式,ofstream使用out模式,fstream使用inout模式。

ofstream out1(fileName); //清除文件内容
ofstream out2(fileName, ofstream::app); //保留文件内容
ofstream out3;
out3.open(fileName, ofstream::app);

string流

把字符串看作是文件。

头文件sstream定义了三个类型来支持内存IO:istringstreamstring数据,ostringstreamstring数据,stringstream则支持双向操作。

特有操作:

  • sstream strmsstream strm(s):构造对象(s是字符串)
  • strm.str():返回strm中所保存的string拷贝
  • strm.str(s):将string s拷贝到strm中,无返回值

当我们的工作是对整行进行处理,并包括处理行内的单个单词时,通常可以使用istringstream

struct PersonInfo {
	string name;
	vector phones;
};
//将控制台输入的姓名和电话号码(可能一个人有多个)信息保存到结构体数组中
string line, word;
vector 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信息作为控制台信息进行读写。

你可能感兴趣的:(C++Primer笔记,c++)