C++(8)IO库

C++语言不直接处理输入输出,而是通过一族定义在标准库中的类型来处理IO。

第八章 IO库

一些简单的IO库设施回顾:

  • istream类型,提供输入操作。
  • ostream类型,提供输出操作。
  • cin,一个istream对象,从标准输入读取数据。
  • cout,一个ostream对象,向标准输出写入数据。
  • cerr,一个ostream对象,输出程序错误信息,写入到标准错误。
  • >>运算符,用来从一个istream对象读取输入的数据。
  • <<运算符,用来向一个ostream对象写入输出数据。
  • getline函数,从一个给定的istream读取一行数据,存入一个给定的string对象中。

一. IO类

  • IO库类型和头文件

    头文件          类型
    iostream        istream, wistream
                    ostream, wostream
                    iostream, wiostream
    
    fstream         ifstream, wifstream
                    ofstream, wofstream
                    fstream, wfstream
    
    sstream         istringstream, wistringstream
                    ostringstream, wostringstream
                    stringstream, wstringsztream
    • 以w开头的类型和对象用来操纵wchar_t类型的数据。(宽字符)
    • 继承机制(inheritance):如ifstream、istringstream均继承自istream。
IO对象无拷贝或赋值
  • 因此也不能将形参或返回类型设置为流类型。通常以引用方式传递或返回流。且传递和返回的引用不能是const的。

条件状态(condition state)

  • IO库条件状态

    strm::iostate       //strm是一种IO类型。iostate是一种极其相关的类型,提供了表达条件状态的完整功能。
    strm::badbit        //用来指出流已崩溃
    strm::failbit       //用来指出一个IO操作失败了
    strm::eofbit        //用来指出流达到了文件结束
    strm::goodbit       //用来指出流未处于错误状态,此值保证为零
    s.eof()             //若流s的eofbit置位,则返回true
    s.fail()            //若流s的failbit或badbit置位,则返回true
    s.bad()             //若流s的badbit置位,则返回true
    s.good()            //若流s处于有效状态,则返回true
    s.clear()           //将流s中所有条件状态复位,将流的状态设置为有效。返回void
    s.clear(flags)      //根据给定的flags标志位,将流s中对应条件状态位复位。flags的类型为strm::iostate。返回void
    s.setstate(flags)   //根据给定的flags标志位,将流s中对应条件状态位置位。flags的类型为strm::iostate。返回void
    s.rdstate()         //返回流s当前的条件状态,返回值类型为strm::iostate
查询流的状态

IO库定义了一个与机器无关的iostate类型,它提供了表达流状态的完整功能。

  • 这个类型应该作为位集合来使用。
  • 与位运算符一起使用来一次性检测或设置多个标志位。
  • badbit表示系统及错误,一旦被置位,流就无法再使用了。
  • 可恢复错误,failbit置位。
  • 到达文件末尾,failbit和eofbit置位。goodbit为0,表示未发生错误。

管理条件状态

//记住 cin 的当前状态
auto old_state = cin.rdstate(); 
cin.clear();                //使 cin 有效
process_input(cin);         //使用 cin
cin.setstate(old_state);    //将 cin 置为原有状态
//复位failbit和babit,保持其他标志位不变
cin.clear(cin.rdstate() & ~cin.failbit & ~cin.badbit);

管理输出缓冲

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

  • 程序正常结束,作为main函数的return操作的一部分,缓冲刷新被执行。
  • 缓冲区满。
  • 使用操纵符endl显示刷新缓冲区。
  • 操纵符unitbuff。cerr默认设置unitbuf。
  • 一个输出流被关联到另一个流。当读写被关联的流时,关联到的流的缓冲区会被刷新。例如,默认情况下,cin和cerr都关联到cout,因此cin或写cerr都会导致cout缓冲区被刷新。
刷新输出缓冲区
  • endl:输出换行,然后刷新缓冲区
  • flush:刷新缓冲区,不附加任何字符
  • ends:输出空格,然后刷新缓冲区
unitbuf操纵符

如果想要在每次输出操作之后都刷新缓冲区,可以使用unitbuf操纵符。

  • 它告诉流在接下来的每次写操作之后都进行一次flush操作。
cout << unitbuf;
//这之间的任何输出都立即刷新
cout << nounitbuf;
  • 如果程序崩溃,输出缓冲区不会刷新。
关联输入和输出流

tie 函数

  • 不带参数:返回指向输出流的指针。对过本对象当前关联到一个输出流,则返回的就是指向这个流的指针;如果对象未关联到流,返回空指针。
  • 带参数:x.tie(&o),将流 x 关联到输出流 o 。
  • 每个流同时最多关联到一个流,但多个流可以同时关联到同一个 ostream。

二. 文件输入输出

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

  • 所有iostream的操作均可使用。除此之外还有以下fstream特有的操作:
fstream fstrm;          // 创建一个未绑定的文件流。fstream是头文件fstream中定义的一个类型
fstream fstrm(s);       // 创建一个fstream,并打开名为s的文件。
                        //s可以是string类型,或者是一个指向C风格字符串的指针。
                        //这些构造函数都是explict的。
                        //默认文件模式mode依赖于fstream的类型。
fstream fstrm(s, mode); //与前一个构造函数类似,但指定mode打开文件
fstrm.open(s);          //打开名为s的文件,并将文件与fstrm绑定。返回void
fstrm.close();          //关闭与fstrm绑定的文件。返回void
fstrm.is_open();        //返回一个bool值,之处与fstrm关联的文件是否成功打开且尚未关闭
使用文件流对象
  • 在要求使用基类型对象的地方,可以使用继承类型的对象来替代。

    • 如,接收iostream类型引用(或指针)参数的函数,可以用一个对应的fstream(或sstream)类型来调用。
    • 读写命名文件,可通过传递给main函数的参数来指定。
  • 成员函数open和close

    • 每当打开文件时,应首先检查open是否成功成功。
    • 为了将文件流关联到另一个文件,必须首先关闭已经关联的文件。
      ifstream in(ifile);
      ofstream out;
      out.open(ifile + ".copy");
      if (out) {}   //检查open是否成功
      in.close();
      in.open(ifile + "2");
  • fstream对象被销毁时,close会自动被调用。

文件模式(file mode)

每个流都有一个关联的文件模式,用来指出如何使用文件。

in      以读的方式打开
out     以写的方式打开
app     每次写操作前均定位到文件末尾(即追加append)
ate     打开文件后立即定位到文件末尾
trunc   截断文件(即打开文件会丢弃已有数据)
binary  以二进制方式进行IO
  • 输出流打开文件时,隐式指定为out和trunc。
  • 若需要不删除已有信息,必须显示的指定app模式。

三. string 流

sstream头文件定义了三个类型来支持内存IO,这些类型可以向string写入数据,从string读取数据,就像string是一个IO流一样。

  • stringstream特有的操作

    sstream strm;       //strm是一个未绑定的stringstream对象。sstream是头文件sstream中定义的一个类型
    sstream strm(s);    //strm是一个sstream对象,保存string s的一个拷贝。此构造函数时explicit的
    strm.str();         //返回strm所保存的string的拷贝
    strm.str(s);        //将string s拷贝到strm中,但会void
  • 使用istringstream

    //输入若干行,每行的一个字符串为名字,后面紧跟n个字符串为电话,每个字符串之前以空格隔开
    //处理这些输入的代码:
    struct  PersonInfo {
        string name;
        vector<string> phones;
    };
    
    string line, word;
    vector<PersonInfo> people;
    while (getline(cin, line)) {
        PersonInfo info;
        istringstream record(line); //将record绑定到刚读入的行
        record >> info.name;        //读取名字
        while (record >> word)      //读取电话号码
            info.phones.push_back(word);
        people.push_back(info);
    }
  • 使用ostringstream

    • 逐步构造输出,希望最后一起打印时,ostringstream很有用。

      //先将输出内容“写入”到一个内存ostringstream中,最后再一起打印
      for (const auto &entry : people) {
          ostringstream formatted, badNums;
          for (const auto &nums : entry.phones) {
              if (!valid(nums)) {
                  badNums << " " << nums;     //将数的字符串形式存入badNums
              } else
                  formatted << " " << format(nums);   //将格式化的字符串存入formatted
          }
          if (badNums.str().empty()) 
              os << entry.name << " "
                 << formatted.str() << endl;
          else
              cerr << "input error: " << entry.name
                   << " invalid number(s) " << badNums.str() << endl;
      }
    • 使用标准输出运算符(<<)向 ostringstream 对象写入数据

小结

  • iostream 处理控制台 IO
  • fstream 处理命名文件 IO
  • stringstream 完成内存 string 的 IO

参考:C++Primer第五版

猜你喜欢

转载自blog.csdn.net/weixu1999/article/details/81634022