1.IO类:
IO 类型在三个独立的头文件中定义:iostream 定义读写控制窗口的类型,fstream 定义读写已命名文件的类型,而 sstream 所定义的类型则用于读写存储在内存中的 string 对象。
标准库类型不允许做复制或赋值操作。
这个要求有两层特别重要的含义。正如在第九章看到的,只有支持复制的元素类型可以存储在 vector 或其他容器类型里。由于流对象不能复制,因此不能存储在 vector(或其他)容器中(即不存在存储流对象的 vector 或其他容器)。
第二个含义是:形参或返回类型也不能为流类型。如果需要传递或返回 IO 对象,则必须传递或返回指向该对象的指针或引用.
一般情况下,如果要传递 IO 对象以便对它进行读写,可用非 const 引用的方式传递这个流对象。对 IO 对象的读写会改变它的状态,因此引用必须是非 const 的。
2.IO 标准库的条件状态
strm::iostate |
Name of the machine-dependent integral type, defined by each iostream class that is used to define the condition states. 机器相关的整型名,由各个 iostream 类定义,用于定义条件状态 |
strm::badbit |
strm::iostate value used to indicate that a stream is corrupted. strm::iostate 类型的值,用于指出被破坏的流 |
strm::failbit |
strm::iostate value used to indicate that an IO operation failed. strm::iostate 类型的值,用于指出失败的 IO 操作 |
strm::eofbit |
strm::iostate value used to indicate the a stream hit end-of-file. strm::iostate 类型的值,用于指出流已经到达文件结束符 |
s.eof() |
true if eofbit in the stream s is set. 如果设置了流 s 的 eofbit 值,则该函数返回 true |
s.fail() |
true if failbit in the stream s is set. 如果设置了流 s 的 failbit 值,则该函数返回 true |
s.bad() |
true if badbit in the stream s is set. 如果设置了流 s 的 badbit 值,则该函数返回 true |
s.good() |
true if the stream s is in a valid state. 如果流 s 处于有效状态,则该函数返回 true |
s.clear() |
Reset all condition values in the stream s to valid state. 将流 s 中的所有状态值都重设为有效状态 |
s.clear(flag) |
Set specified condition state(s) in s to valid. Type of flag is strm::iostate. 将流 s 中的某个指定条件状态设置为有效。flag 的类型是 strm::iostate |
s.setstate(flag) |
Add specified condition to s. Type of flag is strm::iostate. 给流 s 添加指定条件。flag 的类型是 strm::iostate |
s.rdstate() |
Returns current condition of s as an strm::iostate value. 返回流 s 的当前条件,返回值类型为 strm::iostate |
所有流对象都包含一个条件状态成员,该成员由 setstate 和 clear 操作管理。这个状态成员为 iostate 类型,这是由各个 iostream 类分别定义的机器相关的整型。该状态成员以二进制位(bit)的形式使用,类似于第 5.3.1 节的例子中用于记录测验成绩的 int_quiz1 变量。
每个 IO 类还定义了三个 iostate 类型的常量值,分别表示特定的位模式。这些常量值用于指出特定类型的 IO 条件,可与位操作符一起使用,以便在一次操作中检查或设置多个标志。badbit 标志着系统级的故障,如无法恢复的读写错误。如果出现了这类错误,则该流通常就不能再继续使用了。如果出现的是可恢复的错误,如在希望获得数值型数据时输入了字符,此时则设置 failbit 标志,这种导致设置 failbit 的问题通常是可以修正的。eofbit 是在遇到文件结束符时设置的,此时同时还设置了 failbit。
clear 和 setstate 操作用于改变条件成员的状态。clear 操作将条件重设为有效状态。在流的使用出现了问题并做出补救后,如果我们希望把流重设为有效状态,则可以调用 clear 操作。使用 setstate 操作可打开某个指定的条件,用于表示某个问题的发生。除了添加的标记状态,setstate 将保留其他已存在的状态变量不变。
http://hi.baidu.com/jrckkyy/blog/item/ee657b17e49b5703c83d6df7.html
3.输出缓冲区的管理
系统将字符串字面值存储在与流 os 关联的缓冲区中。下面几种情况将导致缓冲区的内容被刷新,即写入到真实的输出设备或者文件:
程序正常结束。作为 main 返回工作的一部分,将清空所有输出缓冲区。
在一些不确定的时候,缓冲区可能已经满了,在这种情况下,缓冲区将会在写下一个值之前刷新。
用操纵符显式地刷新缓冲区,例如行结束符 endl。
在每次输出操作执行完后,用 unitbuf 操作符设置流的内部状态,从而清空缓冲区。
可将输出流与输入流关联(tie)起来。在这种情况下,在读输入流时将刷新其关联的输出缓冲区。
(1)输出缓冲区的刷新:
我们的程序已经使用过 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
如果需要刷新所有输出,最好使用 unitbuf 操纵符。这个操纵符在每次执行完写操作后都刷新流:
cout << unitbuf << "first" << " second" << nounitbuf;
等价于:
cout << "first" << flush << " second" << flush;
nounitbuf 操纵符将流恢复为使用正常的、由系统管理的缓冲区刷新方式。
如果程序崩溃,则不会刷新缓冲区。
调试程序时,必须保证期待写入的每个输出都确实被刷新了。因为系统不会在程序崩溃时自动刷新缓冲区,这就可能出现这样的情况:程序做了写输出的工作,但写的内容并没有显示在标准输出上,仍然存储在输出缓冲区中等待输出。
(2)将输出输入绑定在一起:
当输入流与输出流绑在一起时,任何读输入流的尝试都将首先刷新其输出流关联的缓冲区。标准库将 cout 与 cin 绑在一起,因此语句:
cin >> ival;
导致 cout 关联的缓冲区被刷新。
交互式系统通常应确保它们的输入和输出流是绑在一起的。这样做意味着可以保证任何输出,包括给用户的提示,都在试图读之前输出。
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
4.文件的输入和输出:
文件流对象的初始化:
方式1:
// construct an ifstream and bind it to the file named ifile
ifstream infile(ifile.c_str());
// ofstream output file object to write file named ofile
ofstream outfile(ofile.c_str());
上述代码定义并打开了一对 fstream 对象。infile 是读的流,而 outfile 则是写的流。为 ifstream 或者 ofstream 对象提供文件名作为初始化式,就相当于打开了特定的文件。
方式2:
ifstream infile; // unbound input file stream
ofstream outfile; // unbound output file stream
infile.open("in"); // open file named "in" in the current directory
outfile.open("out"); // open file named "out" in the current directory
上述语句将 infile 定义为读文件的流对象,将 outfile 定义为写文件的对象。这两个对象都没有捆绑具体的文件。在使用 fstream 对象之前,还必须使这些对象捆绑要读写的文件。
打开文件后需检查打开是否成功,这是一个好习惯。
if (!infile) {
cerr << "error: unable to open input file: "
<< ifile << endl;
return -1;
}
fstream 对象一旦打开,就保持与指定的文件相关联。如果要把 fstream 对象与另一个不同的文件关联,则必须先关闭(close)现在的文件,然后打开(open)另一个文件:要点是在尝试打开新文件之前,必须先关闭当前的文件流。open 函数会检查流是否已经打开。如果已经打开,则设置内部状态,以指出发生了错误。接下来使用文件流的任何尝试都会失败。
ifstream infile("in"); // opens file named "in" for reading
infile.close(); // closes "in"
infile.open("next"); // opens file named "next" for reading
如果打算重用已存在的流对象,那么 while 循环必须在每次循环进记得关闭(close)和清空(clear)文件流:
ifstream input;
vector<string>::const_iterator it = files.begin();
// for each file in the vector
while (it != files.end()) {
input.open(it->c_str()); // open the file
// if the file is ok, read and "process" the input
if (!input)
break; // error: bail out!
while(input >> s) // do the work on this file
process(s);
input.close(); // close file when we're done with it
input.clear(); // reset state to ok
++it; // increment iterator to get next file
}
5.文件模式:
表 8.3 文件模式
in |
open for input 打开文件做读操作 |
out |
open output 打开文件做写操作 |
app |
seek to the end before every write 在每次写之前找到文件尾 |
ate |
seek to the end immediately after the open 打开文件后立即将文件定位在文件尾 |
trunc |
truncate an existing stream when opening it 打开文件时清空已存在的文件流 |
binary |
do IO operations in binary mode 以二进制模式进行 IO 操作 |
out、trunc 和 app 模式只能用于指定与 ofstream 或 fstream 对象关联的文件;in 模式只能用于指定与 ifstream 或 fstream 对象关联的文件。所有的文件都可以用 ate 或 binary 模式打开。ate 模式只在打开时有效:文件打开后将定位在文件尾。以 binary 模式打开的流则将文件以字节序列的形式处理,而不解释流中的字符。
默认时,与 ifstream 流对象关联的文件将以 in 模式打开,该模式允许文件做读的操作:与 ofstream 关联的文件则以 out 模式打开,使文件可写。以 out 模式打开的文件会被清空:丢弃该文件存储的所有数据。
outfile2 的定义使用了按位或操作符将相应的文件同时以 out 和 trunc 模式打开。
6.stringstream
有些处理基于每行实现,而其他处理则要操纵每行中每个单词。可用 stringstreams 对象实现:
string line, word; // will hold a line and word from input, respectively
while (getline(cin, line)) { // read a line from the input into line
// do per-line processing
istringstream stream(line); // bind to stream to the line we read
while (stream >> word){ // read a word from line
// do per-word processing
}
}
7. stringstream 提供的转换和/或格式化
stringstream 对象的一个常见用法是,需要在多种数据类型之间实现自动格式化时使用该类类型。例如,有一个数值型数据集合,要获取它们的 string 表示形式,或反之。sstream 输入和输出操作可自动地把算术类型转化为相应的 string 表示形式,反过来也可以。
int val1 = 512, val2 = 1024;
ostringstream format_message;
// ok: converts values to a string representation
format_message << "val1: " << val1 << "/n"
<< "val2: " << val2 << "/n";
这里创建了一个名为 format_message 的 ostringstream 类型空对象,并将指定的内容插入该对象。重点在于 int 型值自动转换为等价的可打印的字符串。format_message 的内容是以下字符:
val1: 512/nval2: 1024
相反,用 istringstream 读 string 对象,即可重新将数值型数据找回来。读取 istringstream 对象自动地将数值型数据的字符表示方式转换为相应的算术值。
// str member obtains the string associated with a stringstream
istringstream input_istring(format_message.str());
string dump; // place to dump the labels from the formatted message
// extracts the stored ascii values, converting back to arithmetic types
input_istring >> dump >> val1 >> dump >> val2;
cout << val1 << " " << val2 << endl; // prints 512 1024
这里使用 。str 成员获取与之前创建的 ostringstream 对象关联的 string 副本。再将 input_istring 与 string 绑定起来。在读 input_istring 时,相应的值恢复为它们原来的数值型表示形式
为了读取 input_string,必须把该 string 对象分解为若干个部分。我们要的是数值型数据;为了得到它们,必须读取(和忽略)处于所需数据周围的标号。
(不理解)