码迷,mamicode.com
首页 > 其他好文 > 详细

标准IO库

时间:2016-02-18 19:37:06      阅读:164      评论:0      收藏:0      [点我收藏+]

标签:

  • IO标准库类型和头文件

技术分享

技术分享

出于某些原因,标准库类型不允许做复制或赋值操作。

ofstream out1, out2;
    out1 = out2;   // error: cannot assign stream objects
    // print function: parameter is copied
    ofstream print(ofstream);
    out2 = print(out2);  

这个要求有两层特别重要的含义。只有支持复制的元素类型可以存储在 vector 或其他容器类型里。由于流对象不能复制,因此不能存储在 vector(或其他)容器中(即不存在存储流对象的 vector 或其他容器)

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

ofstream &print(ofstream&);              // ok: takes a reference, no copy
    while (print(out2)) { /* ... */ }

一般情况下,如果要传递 IO 对象以便对它进行读写,可用非 const 引用的方式传递这个流对象。对 IO 对象的读写会改变它的状态,因此引用必须是非 const 的。

  • 条件状态

IO 标准库管理一系列条件状态(condition state)成员,用来标记给定的 IO 对象是否处于可用状态,或者碰到了哪种特定的错误。

技术分享

考虑下面 IO 错误的例子:

 int ival;
     cin >> ival;

如果在标准输入设备输入 Borges,则 cin 在尝试将输入的字符串读为 int 型数据失败后,会生成一个错误状态。类似地,如果输入文件结束符(end-of-file),cin 也会进入错误状态。而如果输入 1024,则成功读取,cin 将处于正确的无错误状态。

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

if (cin)
               // ok to use cin, it is in a valid state

          while (cin >> word)
               // ok: read operation successful ...

if 语句直接检查流的状态,而 while 语句则检测条件表达式返回的流,从而间接地检查了流的状态。如果成功输入,则条件检测为 true

许多程序只需知道是否有效。而某些程序则需要更详细地访问或控制流的状态,此时,除了知道流处于错误状态外,还必须了解它遇到了哪种类型的错误。例如,程序员也许希望弄清是到达了文件的结尾,还是遇到了 IO 设备上的错误。

所有流对象都包含一个条件状态成员,该成员由 setstateclear 操作管理。这个状态成员为 iostate 类型,这是由各个 iostream 类分别定义的机器相关的整型。该状态成员以二进制位(bit)的形式使用。

每个 IO 类还定义了三个 iostate 类型的常量值,分别表示特定的位模式。这些常量值用于指出特定类型的 IO 条件,可与位操作符一起使用,以便在一次操作中检查或设置多个标志。

badbit 标志着系统级的故障,如无法恢复的读写错误。如果出现了这类错误,则该流通常就不能再继续使用了。如果出现的是可恢复的错误,如在希望获得数值型数据时输入了字符,此时则设置 failbit 标志,这种导致设置 failbit 的问题通常是可以修正的。eofbit 是在遇到文件结束符时设置的,此时同时还设置了 failbit

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

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

可以如下管理输入操作

int ival;
    // read cin and test only for EOF; loop is executed even if there are other IO failures
    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
    }

这个循环不断读入 cin,直到到达文件结束符或者发生不可恢复的读取错误为止。循环条件使用了逗号操作符:首先计算它的每一个操作数,然后返回最右边操作数作为整个操作的结果。因此,循环条件只读入 cin 而忽略了其结果。该条件的结果是 !cin.eof() 的值。如果 cin 到达文件结束符,条件则为假,退出循环。如果 cin 没有到达文件结束符,则不管在读取时是否发生了其他可能遇到的错误,都进入循环。

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

// sets both the badbit and the failbit
   is.setstate(ifstream::badbit | ifstream::failbit);
  • 输出缓存区的管理

每个 IO 对象管理一个缓冲区,用于存储程序读写的数据。如有下面语句:

os << "please enter a value: ";

系统将字符串字面值存储在与流 os 关联的缓冲区中。下面几种情况将导致缓冲区的内容被刷新,即写入到真实的输出设备或者文件:

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

我们的程序已经使用过 endl 操纵符,用于输出一个换行符并刷新缓冲区。除此之外,C++ 语言还提供了另外两个类似的操纵符。第一个经常使用的 flush,用于刷新流,但不在输出中添加任何字符。第二个则是比较少用的 ends,这个操纵符在缓冲区中插入空字符 null,然后后刷新它:

cout << "hi!" << flush;      // flushes the buffer; adds no data
    cout << "hi!" << ends;       // inserts a null, then flushes the buffer
    cout << "hi!" << endl;       // inserts a newline, then flushes the buffer

//cout是输出语句,flush是缓冲区的内容,cout<<flush表示将缓冲区的内容马上送进cout,把输出缓冲区刷新

当程序向输出设备中输出数据时,输出的数据先被存放在计算机缓 冲区(Buffer)内。当缓冲区存满时,这些数据才真正地输出到输出设备。但是,如果输出的字符序列中出现了 endl控制符,那么缓冲区内的所有数据将立即输出到输出设备,而无论缓冲区是否已经存满。因此,endl控制符的作用是将光标移动到输出设备中下一行开 头处,并且清空缓冲区。很有可能出现在程序终止时,并没有输出所有的输出数据的情况。这是因为在程序终止时,缓冲区并不一定是满的,所以也就没有将缓冲区中的数据写到输出设备。在C++中,可以使用flush函数来清空缓冲区,即使缓冲区中的数据不是满的。与endl控制符不同的是,flush函数并不是把光标移到下一行的开头处。总之,flush和endl的功能一样,只是前者不换行而已。

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

cout << unitbuf << "first" << " second" << nounitbuf;

等价于:

cout << "first" << flush << " second" << flush;

nounitbuf 操纵符将流恢复为使用正常的、由系统管理的缓冲区刷新方式。

警告:如果程序崩溃了,不会刷新缓冲区。如果程序不正常结束,输出缓冲区将不会刷新。在尝试调试已崩溃的程序时,通常会根据最后的输出找出程序发生错误的区域。如果崩溃出现在某个特定的输出语句后面,则可知是在程序的这个位置之后出错。调试程序时,必须保证期待写入的每个输出都确实被刷新了。因为系统不会在程序崩溃时自动刷新缓冲区,这就可能出现这样的情况:程序做了写输出的工作,但写的内容并没有显示在标准输出上,仍然存储在输出缓冲区中等待输出。如果需要使用最后的输出给程序错误定位,则必须确定所有要输出的都已经输出。为了确保用户看到程序实际上处理的所有输出,最好的方法是保证所有的输出操作都显式地调用了 flushendl如果仅因为缓冲区没有刷新,程序员将浪费大量的时间跟踪调试并没有执行的代码。基于这个原因,输出时应多使用 endl 而非 ‘\n‘。使用 endl 则不必担心程序崩溃时输出是否悬而未决(即还留在缓冲区,未输出到设备中)。

  • 文件的输入和输出

fstream 头文件定义了三种支持文件 IO 的类型:ifstream,由 istream 派生而来,提供读文件的功能;ofstream,由 ostream 派生而来,提供写文件的功能;fstream,由 iostream 派生而来,提供读写同一个文件的功能。

这些类型都由相应的 iostream 类型派生而来,这个事实意味着我们已经知道使用 fstream 类型需要了解的大部分内容了。特别是,可使用 IO 操作符(<<>> )在文件上实现格式化的 IO。fstream 类型除了继承下来的行为外,还定义了两个自己的新操作—— openclose,以及形参为要打开的文件名的构造函数

标准IO库

标签:

原文地址:http://www.cnblogs.com/predator-wang/p/5199038.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!