c++标准I/O总结

2017-08-21  本文已影响0人  lwj_ow

之前我们总结了Unix系统I/O和Unix标准I/O,这次我们就顺便总结一下C++I/O库,主要内容都来自C++ primer, 这也算是对C++ primer一个复习吧.

  1. IO类


    IO库类型和头文件

    标准库使我们能忽略这些不同类型的流之间的差异,这是通过继承机制实现的.简单来说, 继承机制使我们可以声明一个特定的类继承自另一个类.我们通常可以将一个派生类(继承类)对象当做其基类(所继承的类)对象来使用.
    类型ifstream和istringstream都继承自istream.因此,我们可以像使用istream对象一样来使用ifstream和istringstream对象.例如,我们可以对一个ifstream或istringstream对象调用getline, 也可以使用>>从一个ifstream或istringstream对象中读取数据.类似的,ofstream和ostringstream都继承自ostream.同理,我们是如果使用cout的,就可以同样的使用这些类型的对象.

    1. IO对象无拷贝或赋值
      这是初学者非常容易犯的一个错误,经常在将IO对象作为函数参数时忘记加上引用符号,导致错误却摸不着头脑..
      另外,读写一个IO对象会改变其状态,所以传递和返回的引用也不能是const的.

    2. 条件状态


      image.png

      一旦一个流发生错误,其上后续的IO操作都会失败.只有当一个流处于无错状态,我们才能对其进行读写.确定一个流对象的状态的最简单的方法是将其作为一个条件来使用.

      while(cin>>word)
      //ok: 操作成功

      while循环检查>>表达式返回的流的状态.如果输入操作成功,流保持有效状态,则条件为真.

      badbit表示系统级错误,如不可恢复的读写错误.通常情况下,如果badbit被设置了,流就无法使用了.在发生可恢复错误之后,failbit被置位,如期望读取数值却读出一个字符等错误,这种问题通常是可以修正的,流还可以继续使用.如果到达文件尾端,eofbit和failbit都会被置位.goobit的值为0,表示流未发生错误.

      标准库还定义了一组函数来查询或设置这些标志位的状态, 这些函数在上面的图中已经有了,我也不再赘述.

    3. 管理输出缓冲
      我们在前面总结Unix标准I/O的时候就已经知道了,这些非系统I/O都人为增加缓冲区来提高I/O效率,C++标准I/O也不例外.

      导致缓冲刷新(即,数据真正写到输出设备或文件)的原因有很多:

      • 程序正常结束,作为main函数return的一部分.
      • 缓冲区满时,要刷新缓冲,新的数据才能写入.
      • 可以使用操纵符如endl来显式刷新缓冲区.
      • 在每个输出操作之后,我们可以用操纵符unitbuf设置流的内部状态,来清空缓冲区.默认情况下,对cerr就是设置unitbuf的.
      • 一个输出流可能会被关联到另一个流,这种情况下,当读写被关联的流时,关联到流的缓冲区会被刷新.例如,默认情况下,cin和cerr都关联到cout,所以,读cin或者写cerr都会导致cout的缓冲区被刷新.

      刷新输出缓冲区

      cout<<"hi"<<endl; //输出hi和一个换行,然后刷新缓冲区
      cout<<"hi"<<flush; //输出hi,然后刷新缓冲区
      cout<<"hi"<<ends; //输出hi和一个空字符,然后刷新缓冲区

      unitbuf操纵符

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

      警告:如果程序崩溃,输出缓冲区是不会被刷新的.因此我们的数据很有可能停留在输出缓冲区中等待打印.所以,给我们的教训是:当调试一个崩溃的程序时,需要确认那些你认为已经输出的数据确实已经刷新了,否则可能数据已经输出,但是停留在缓冲区内没有打印出来.

      关联输入和输出流
      标准库将cout和cin关联在一起,因此读cin会导致cout的缓冲区被刷新.注:交互式系统通常应该关联输入流和输出流.这就意味着所有输出,包括用户提示信息,都会在读操作之前被打印出来.

      tie函数有两个重载的版本:一个版本不带参数,返回指向输出流的指针.如果本对象当前关联到一个输出流,则返回的就是指向这个流的指针,如果对象未关联到流,则返回空指针,tie的第二个版本接受一个指向ostream的指针,将自己关联到此ostream.

  2. 文件输入输出

    fstream.h头文件定义了三个支持文件的类型:ifstream用来读文件,ofstream用来写入文件,fstream用来读写文件.这些类型提供了与我们之前用在cin和cout上相同的操作.另外,我们也可以使用IO操作符(>>和<<)来读写文件,同样的我们也可以将他们从基类其他继承过来的IO操作用在这三种类型上.
    由于这三种类型是与文件打交道的,所以他们也有一些特有的文件IO操作,这些操作并不能被其他IO类型使用.如下图所示:

    image.png

    文件模式
    每个流都有一个关联的文件模式,用来指出如何使用文件.下表列出了文件模式和它们的含义.

    image.png

    无论用哪种方式来打开文件,我们都可以指定文件模式,调用open打开文件时可以,用一个文件名初始化流来隐式打开文件时也可以.指定文件模式有以下限制:

    • 只可以对ofstream或fstream对象设定out模式.
    • 只可以对ifstream或fstream对象设定in模式.
    • 只有当out也被设定时才可以设定trunc模式/
    • 只要trunc没被设定,就可以设定app模式.在app模式下,即使灭有显式指定out模式,文件也总是以输出方式被打开.
    • 默认情况下,即使我们没有指定trunc,以out模式打开的文件也会被截断.为了保留以out模式打开的文件的内容,我们必须同时指定app模式,这样只会将数据追加到文件末尾;或者同时指定in模式,即打开文件同时指定读写操作.
    • ate和binary模式可用于任何类型的文件流对象,且可以与其他任何文件模式组合使用.

    每个文件流类型都定义了一个默认的文件模式,当我们未指定文件模式时,就使用此默认模式.与ifstream关联的文件默认以in模式打开;与ofstream关联的文件模式以out模式打开;与fstream关联的文件默认以in和out模式打开.

  3. string流
    sstream头文件定义了三个类型来支持内存IO,这些类型可以向string写入数据,从string读取数据,就像string是一个IO流一样.这三个类型分别是istringstream,ostringstream和stringstream,用法可以类比三个文件IO类型,不再细说.

    另外是stringstream特有的操作.如下表所示:


    image.png

    istringstream
    当我们的工作是处理某一行的单个单词时,istringstream就显得十分的重要了.举个小例子(来自c++ primer):
    假设一个文件,列出了一些人和他们的电话号码,有些人可能有多个电话号码.文件看起来如下:
    jack 123456 154679
    lucy 789453 493716
    tony 456712 791846
    文件格式都是如此.

    我们首先定义一个结构体来描述输入数据:

    struct PersonInfo{
        strting name;
        vector<string> phone;
    }
    

    再写如下程序来处理文件:

    string line, word;  // 分别保存来自输入的一行和单词
    vector<PersionInfo> people;//保存来自输入的所有记录
    //逐行从输入读取数据,直至cin遇到文件尾(或其他错误)
    while(getline(cin,line))
    {
        Persioninfo info;//创建一个保存此记录数据的对象
        istringstream record(line);//将记录绑定到刚读入的行
        record>>info.name;//读取名字
        while(record >> word)//读取电话号码
            info.phones.push_back(word);//保存到容器中
        people.push_back(info);//将此记录追加到people末尾
    }
    

    代码并不难理解,也有注释,我就不在这里赘述了.

    ostringstream
    对于ostringstream来说,用法和istringstream是有类似之处的,我们可以使用<<运算符来将数据写入ostringstream对象.例如:

    int a = 1234;
    ostringstream out;
    out<<a<<"hello";
    cout<<out.str<<endl;

    这段代码就会打印1234hello.用法不难,用处比较专一,也很有用.

结语:写到这里,C++的标准IO总结就差不多了,这篇文章写的挺早,不过中间出去比赛,一直就没怎么动,到了今天才把完全写完.

上一篇下一篇

猜你喜欢

热点阅读