C++ primer 学习笔记(第八章)

标准IO库

C++使用标准库类处理输入和输出:

  • iostream类处理面向流的输入和输出
  • fstream类处理已命名文件的IO
  • stringstream类处理内存中字符串的IO

所有这些类都是通过继承相互关联的。输入类继承了istream,输出类继承了ostream。因此,可在istream对象上执行的操作同样适用于ifstream或istringstream对象。而继承ostream的输出类也是类似的。

所有IO对象都有一组条件状态,用来指示是否可以通过该对象进行IO操作。如果出现了错误(例如遇到文件结束符)对象的状态将标志无法再进行输入,直到修正了错误为止。标准库提供了一组函数设置和检查这些状态。

面向对象的标准库
  • C++中简单的iostream继承层次

  • IO对象不可复制或赋值

    ofstream out1, out2;
    out1 = out2;   //error,不能赋值
    
    ofstream print(ofstream);
    out2 = print(out2);  //error,不能复制
    

    这个要求的两层含义:

    • 由于流对象不能复制,因此不能存储在vector(或其他)容器中

    • 形参或返回类型也不能为流类型。如果需要返回或传递IO对象,则必须传递或返回指向该对象的指针或引用:

      ofstream &print(ofstream&);
      while (print(out2)) {/*...*/}
      //一般对IO对象的读写会改变它的状态,因此引用必须是非const的
      
条件状态

IO 标准库管理一系列条件状态(condition state)成员,用来标记给定的 IO 对象是否处于可用状态,或者碰到了哪种特定的错误。下表列出了标准库定义的一组函数和标记,提供访问和操纵流状态的手段。

条件状态作用
strm::iostate机器相关的整型名,由各个 iostream 类定义,用于定义条件状态
strm::badbitstrm::iostate 类型的值,用于指出被破坏的流
strm::failbitstrm::iostate 类型的值,用于指出失败的 IO 操作
strm::eofbitstrm::iostate 类型的值,用于指出流已经到达文件结束符
s.eof()如果设置了流 s 的 eofbit 值,则该函数返回 true
s.fail()如果设置了流 s 的 failbit 值,则该函数返回 true
s.bad()如果设置了流 s 的 badbit 值,则该函数返回 true
s.good()如果流 s 处于有效状态,则该函数返回 true
s.clear()将流 s 中的所有状态值都重设为有效状态
s.clear(flag)将流 s 中的某个指定条件状态设置为有效。flag 的类型是strm::iostate
s.setstate(flag)给流 s 添加指定条件。flag 的类型是 strm::iostate
s.rdstate()返回流 s 的当前条件,返回值类型为 strm::iostate

流必须处于无错误状态,才能用于输入或输出。检测流是否用的最简单的方法是检查其真值

if (cin)    // ok to use cin, it is in a valid state
    
while (cin >> word)  // ok: read operation successful ...
//if 语句直接检查流的状态,而 while 语句则检测条件表达式返回的流,从而间接地检查了流的状态。如果成功输入
//条件检测为 true。
  • 如何访问或控制流的状态,以便知道是达到了文件结尾,还是遇到了IO设备上的错误

    流的状态由 bad、fail、eof 和 good 操作提示。如果 bad、fail 或者 eof中的任意一个为 true,则检查流本身将显示该流处于错误状态。类似地,如果这三个条件没有一个为 true,则 good 操作将返回 true。

    clear 和setstate 操作用于改变条件成员的状态。clear 操作将条件重设为有效状态。在流的使用出现了问题并做出补救后,如果我们希望把流重设为有效状态,则可以调用 clear 操作。使用 setstate 操作可打开某个指定的条件,用于表示某个问题的发生。除了添加的标记状态,setstate 将保留其他已存在的状态变量不变。(也就是出了问题时用来设置状态的)

  • 流状态的查询和控制

    int ival;
    //这个循环不断读入 cin,直到到达文件结束符或者发生不可恢复的读取错误为止。
    while (cin >> ival, !cin.eof()) {
    	if (cin.bad())                     // input stream is corrupted; bail out
    		throw runtime_error("IO stream corrupted");
    	if (cin.fail()) {              	   // bad input
    		cerr<< "bad data, try again";  // warn the user
    		cin.clear(istream::failbit);   // reset the stream
    		continue;                      // get next input
    	}
    // ok to process ival
    }
    
  • 条件状态的访问

    使用rdstate成员函数,它返回一个iostate类型的值,对应流当前的整个条件状态:

    // remember current state of cin
    istream::iostate old_state = cin.rdstate();
    cin.clear();
    process_input(); // use cin
    cin.clear(old_state); // now reset cin to old state
    
  • 多种状态的处理

    通过多次调用 setstate 或者 clear 函数实现。

    is.setstate(ifstream::badbit | ifstream::failbit)
    

    另外一种方法则是使用按位或(OR)操作符在一次调用中生成“传递两个或更多状态位”的值。按位或操作使用其操作数的二进制位模式产生一个整型数值。对于结果中的每一个二进制位,如果其值为 1,则该操作的两个操作数中至少有一个的对应二进制位是 1。

    is.badbit | is.failbit
    
输出缓冲区的管理

每个IO 对象管理一个缓冲区,用于存储程序读写的数据;

下面几种情况将导致缓冲区的内容被刷新,即写入到真实的输出设备或者文件:

  1. 程序正常结束。作为 main 返回工作的一部分,将清空所有输出缓冲区。
  2. 在一些不确定的时候,缓冲区可能已经满了,在这种情况下,缓冲区将会在写下一个值之前刷新。
  3. 操纵符显式地刷新缓冲区,例如行结束符 endl。
  4. 在每次输出操作执行完后,用 unitbuf 操作符设置流的内部状态,从而清空缓冲区。
  5. 可将输出流与输入流关联(tie)起来。在这种情况下,在读输入流时将刷新其关联的输出缓冲区
  • 输出缓冲区的刷新

    cout << "hi!" << flush; // 用于刷新流,但不在输出中添加任何字符
    cout << "hi!" << ends; // 在缓冲区中插入空字符 null,然后后刷新它
    cout << "hi!" << endl; // 输出一个换行符并刷新缓冲区
    
  • unitbuf 操纵符

    如果需要刷新所有输出,最好使用 unitbuf操纵符。这个操纵符在每次执行完写操作后都刷新流:

    cout << unitbuf << "first" << " second" << nounitbuf;
    //等价于
    cout << "first" << flush << " second" << flush;
    //nounitbuf操纵符将流恢复为使用正常的、由系统管理的缓冲区刷新方式。
    

    注意:如果程序崩溃了,则不会刷新缓冲区

    输出时应多使用 endl 而非 ‘\n’。使用endl 则不必担心程序崩溃时输出是否悬而未决(即还留在缓冲区,未输出到设备中)。

  • 将输入和输出绑在一起(tie)

    当输入流与输出流绑在一起时,任何读输入流的尝试都将首先刷新其输出流关联的缓冲区,可以保证任何输出,包括给用户的提示,都在试图读之前输出,

    //tie 函数可用 istream 或 ostream 对象调用,使用一个指向 ostream 对象的指针形参。调用 tie 函
    //数时,将实参流绑在调用该函数的对象上。如果一个流调用 tie 函数将其本身绑在传递给 tie 的 ostream
    //实参对象上,则该流上的任何 IO 操作都会刷新实参所关联的缓冲区。
    
    cin.tie(&cout);   // illustration only: the library ties cin and cout for us
    ostream *old_tie = cin.tie();   //返回指向绑定的输出流的指针。
    cin.tie(0);      // break tie to cout, cout no longer flushed when cin is read 解除绑定
    cin.tie(&cerr);  // ties cin and cerr, not necessarily a good idea!
    // ...
    cin.tie(0);      // break tie between cin and cerr  解除绑定
    cin.tie(old_tie); // restablish normal tie between cin and cout
    
    //一个 ostream 对象每次只能与一个 istream 对象绑在一起。如果在调用tie 函数时传递实参 0,则打破该
    //流上已存在的捆绑。
    
文件的输入和输出
  • 文件流对象的使用

    需要读写文件时,必须定义自己的对象,并将它们绑定在需要的文件上

    ifstream infile(ifile.c_str());  //将输入流对象infile绑定到文件ifile
    ofsrteam outfile(ofile.c_str()); //将输出流对象outfile绑定到文件ofile
    
    //在使用fstream对象之前,还必须使这些对象捆绑要读写的文件,调用open成员函数即可
    infile.open("in");
    outfile.open("out")
    
    • 检查文件是否打开成功

      if(!infile) {
      	cerr << "error: unable to open input file:"
      		 << infile <<endl;
      	return -1;
      }
      
    • 将文件流与新文件重新捆绑

      需要先关闭(close)现在的文件,然后打开(open)另一个文件:

      ifstream infile("in");
      infile.close();
      infile.open("next");
      
    • 清除文件流的状态

      ifstream input;  //将input定义到while之外避免每次循环都创建新的流对象
      vector<string>::const_iterator it = files.begin(); //vector的元素为文件名
      while (it != files.end()) {
          input.open(it->c_str());  //打开文件
          
          if(!input)
              break;
          while (input >> s)
              process(s);
          input.close();   //当处理完该文件,关闭
          input.clear();   //重新恢复流的状态,等同于重新创建了该对象,这样就可以重用已存在的流对象
          ++it;
      }
      

      注意:如果需要重用文件流读写多个文件,必须在读另一个文件之前调用clear清除该流的状态,以免之前的错误信息影响下一次input操作

  • 文件模式

    in打开文件做读操作
    out打开文件做写操作
    app在每次写之前找到文件尾
    ate打开文件后立即将文件定位到文件尾
    trunc打开文件时清空已存在的文件流
    binary以二进制模式进行IO操作

    out、trunc、app模式只能用于指定与ofstream或fstream对象关联的文件;

    in模式只能用于指定与ifstream或fstream对象关联的文件;

    所有文件都能以ate和binary模式打开;

    为ofstream对象指定out模式等效于同时指定了out和trunc模式;

    对于用ofstream打开的文件,要保存文件中已存在的数据,唯一方法时显式指定app模式打开

    • 同一个文件作输入和输出运算

      fstream对象既可以读也可以写它所关联的文件

      fstream inOut("copyOut", fstream::in | fstream::out);
      
    • 模式是文件的属性而不是流的属性

      只要调用open函数,就要设置文件模式,其模式的设置可以是显式的,也可以是隐式的。如果没有指定文件模式,将使用默认值。

    • 打开模式的组合

      out打开文件做写操作,删除文件中已有的数据
      out | app打开文件做写操作,在文件尾写入
      out | trunc与out模式相同
      in打开文件做读操作
      in | out打开文件做读、写操作,并定位于文件开头处
      in | out | trunc打开文件做读、写操作,删除文件中已有的数据
    • 打开并检查输入文件的程序

      ifstream& open_file(ifstream &in, const string &file){
          in.close();		//关闭以存在的文件流
          in.clear();		//清除错误信息
          //由于不清楚in当前状态,首先调用close和clear将这个流设置为有效状态
          in.open(file.c_str());
          return in;
      }
      
字符串流

三种类型:

  1. istringstream, 由istream派生而来,提供读string的功能
  2. ostringstream, 由ostream派生而来,提供写string的功能
  3. stringstream, 由iostream派生而来,提供读写string的功能
stringstream strm;创建自由的stringstream对象
stringstream strm(s);创建存储s的副本的stringstream对象,其中s是string类型的对象
strm.str()返回strm中存储的string类型对象
strm.str(s)将string类型的s复制给strm,返回void
  • stringstream对象的使用

    结合string输入操作符和getline函数,将istringstream对象与所读取的行绑定起来,这样只需使用普通的string输入操作符即可读出每行中的单词:

    string line, word;
    while (getline(cin, line)) {  //从输入缓冲区读取一行赋给line
        //处理每行
        istringstream stream(line);  //将line对象与stream绑定,读取line对象
        while (stream >> word) {	//从line对象中读取单个单词
            //processing 
        }
    }
    
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值