C++笔记——标准IO库
2014-06-13 14:58
447 查看
转自:http://hahack.com/wiki/cpp-standard-io.html
面向对象的标准库
IO 类型在三个独立的头文件中定义:Header | Type |
---|---|
iostream | istream 从流中读取 |
ostream 写到流中去 | |
iostream 对流进行读写;从 istream 和 ostream 派生而来 | |
fstream | ifstream 从文件中读取;由 istream 派生而来 |
ofstream 写到文件中去;由 ostream 派生而来 | |
fstream 读写文件;由 iostream 派生而来 | |
sstream | istringstream 从 string 对象中读取;由 istream 派生而来 |
ostringstream 写到 string 对象中去;由 ostream 派生而来 | |
stringstream 对 string 对象进行读写;由 iostream 派生而来 |
IO 对象不可复制或赋值
出于某些原因,标准库类型不允许做复制或赋值操作。1 2 3 4 5 | ofstream out1, out2; out1 = out2; // error: cannot assign stream objects // print function: parameter is copied ofstream print(ofstream); out2 = print(out2); // error: cannot copy stream objects |
只有支持复制的元素类型可以存储在 vector 或其他容器类型里。由于流对象不能复制,因此不能存储在 vector(或其他)容器中(即不存在存储流对象的 vector 或其他容器)。
形参或返回类型也不能为流类型。如果需要传递或返回 IO 对象,则必须传递或返回指向该对象的指针或引用:
1 2 | ofstream &print(ofstream&); // ok: takes a reference, no copy while (print(out2)) { /* ... */ } // ok: pass reference to out2 |
文件流
fstream 头文件定义了三种支持文件 IO 的类型:ifstream,由 istream 派生而来,提供读文件的功能。
ofstream,由 ostream 派生而来,提供写文件的功能。
fstream,由 iostream 派生而来,提供读写同一个文件的功能。
这些类型都由相应的 iostream 类型派生而来,这个事实意味着我们已经知道使用 fstream 类型需要了解的大部分内容了。特别是,可使用 IO 操作符
<<和
>>在文件上实现格式化的
IO,而且在前面章节介绍的条件状态也同样适用于 fstream 对象。
fstream 类型除了继承下来的行为外,还定义了两个自己的新操作—— open 和 close,以及形参为要打开的文件名的构造函数。fstream、ifstream 或 ofstream 对象可调用这些操作,而其他的 IO 类型则不能调用。
文件流对象的使用
1 23 | // construct an ifstream and bind it to the file named ifile ifstream infile("in"); // ofstream output file object to write file named ofile ofstream outfile("out"); |
下面的代码与上述代码等效:
1 23 | 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 |
由于历史原因,IO 标准库使用 C 风格字符串而不是 C++ strings 类型的字符串作为文件名。在创建 fstream 对象时,如果调用 open 或使用文件名作初始化式,需要传递的实参应为 C 风格字符串,而不是标准库 strings 对象。程序常常从标准输入获得文件名。通常,比较好的方法是将文件名读入 string 对象,而不是 C 风格字符数组。假设要使用的文件名保存在 string 对象中,则可调用
c_str成员获取
C 风格字符串。
例如,假设 ifile 和 ofile 是存储希望读写的文件名的 strings 对象,可如下编写代码:
1 2 | infile.open(str1.c_str()); // open file named "in" in the current directory outfile.open(str2.c_str()); // open file named "out" in the current directory |
检查文件打开是否成功
打开文件后,通常要检验打开是否成功,这是一个好习惯:1 2 3 4 56 | // check that the open succeeded if (!infile) { cerr << "error: unable to open input file: " << ifile << endl; return -1; } |
1 | if (outfile) // ok to use outfile? |
1 | if (!outfile) // not ok to use outfile? |
将文件流与新文件重新捆绑
fstream 对象一旦打开,就保持与指定的文件相关联。如果要把 fstream 对象与另一个不同的文件关联,则必须 先关闭 (close)现在的文件,然后 再打开 (open)另一个文件:要点是在尝试打开新文件之前,必须先关闭当前的文件流。open 函数会检查流是否已经打开。如果已经打开,则设置内部状态,以指出发生了错误。接下来使用文件流的任何尝试都会失败。
1 23 | ifstream infile("in"); // opens file named "in" for reading infile.close(); // closes "in" infile.open("next"); // opens file named "next" for reading |
文件模式
在打开文件时,无论是调用 open 还是以文件名作为流初始化的一部分,都需指定文件模式(file mode)。每个 fstream 类都定义了一组表示不同模式的值,用于指定流打开的不同模式。与条件状态标志一样,文件模式也是整型常量,在打开指定文件时,可用位操作符设置一个或多个模式。文件流构造函数和 open 函数都提供了默认实参设置文件模式。默认值因流类型的不同而不同。此外,还可以显式地以模式打开文件。in | 打开文件做读操作 |
out | 打开文件做写操作 |
app | 追加模式:在每次写之前找到文件尾 |
ate | 打开文件后立即将文件定位在文件尾 |
trunc | 清空模式:打开文件时清空已存在的文件流 |
binary | 以二进制模式进行 IO 操作 |
out | 打开文件做写操作,删除文件中已有的数据 |
out | app | 打开文件做写操作,在文件尾写入 |
out | trunc | 与 out 模式相同 |
in | 打开文件做读操作 |
in | out | 打开文件做读、写操作,并定位于文件开头处 |
in | out | trunc | 打开文件做读、写操作,删除文件中已有的数据 |
默认时,与 ifstream 流对象关联的文件将以 in 模式打开,该模式允许文件做读的操作:与 ofstream 关联的文件则以 out 模式打开,使文件可写。以 out 模式打开的文件会被清空:丢弃该文件存储的所有数据。
从效果来看,为 ofstream 对象指定 out 模式等效于同时指定了 out 和 trunc 模式。
对于用 ofstream 打开的文件,要保存文件中存在的数据,唯一方法是显式地指定 app 模式打开:
1 2 3 4 56 | // output mode by default; truncates file named "file1" ofstream outfile("file1"); // equivalent effect: "file1" is explicitly truncated ofstream outfile2("file1", ofstream::out | ofstream::trunc); // append mode; adds new data at end of existing file named "file2" ofstream appfile("file2", ofstream::app); |
对同一个文件作输入和输出运算
fstream 对象既可以读也可以写它所关联的文件。fstream 如何使用它的文件取决于打开文件时指定的模式。默认情况下,fstream 对象以 in 和 out 模式同时打开。 当文件同时以 in 和 out 打开时不清空 。如果打开 fstream 所关联的文件时,只使用 out 模式,而不指定 in 模式,则文件会清空已存在的数据。如果打开文件时指定了 trunc 模式,则无论是否同时指定了 in 模式,文件同样会被清空。下面的定义将 copyOut 文件同时以输入和输出的模式打开:
1 2 | // open for input and output fstream inOut("copyOut", fstream::in | fstream::out); |
示例:一个打开并检查输入文件的程序
我们编写一个名为 open_file的函数实现这个功能。这个函数有两个引用形参,分别是
ifstream 和 string 类型,其中 string 类型的引用形参存储与指定 ifstream 对象关联的文件名:
1 2 3 4 56 | // opens in binding it to the given file ifstream& open_file(ifstream &in, const string &file) { in.close(); // close in case it was already open in.clear(); // clear any existing errors // if the open fails, the stream will be in an invalid state in.open(file.c_str()); // open the file we were given return in; // condition state is good if open succeeded } |
字符串流
标准库定义了三种类型的字符串流:istringstream,由 istream 派生而来,提供读 string 的功能。
ostringstream,由 ostream 派生而来,提供写 string 的功能。
stringstream,由 iostream 派生而来,提供读写 string 的功能。
要使用上述类,必须包含 sstream 头文件。
与 fstream 类型一样,上述类型由 iostream 类型派生而来,这意味着 iostream 上所有的操作适用于 sstream 中的类型。sstream 类型除了继承的操作外,还各自定义了一个有 string 形参的构造函数,这个构造函数将 string 类型的实参复制给 stringstream 对象。对 stringstream 的读写操作实际上读写的就是该对象中的 string 对象。这些类还定义了名为 str 的成员,用来读取或设置 stringstream 对象所操纵的 string 值。
stringstream strm; | 创建自由的 stringstream 对象 |
stringstream strm(s); | 创建存储 s 的副本的 stringstream 对象,其中 s 是 string 类型的对象 |
strm.str() | 返回 strm 中存储的 string 类型对象 |
strm.str(s) | 将 string 类型的 s 复制给 strm,返回 void |
定义和使用
前面已经见过以每次一个单词或每次一行的方式处理输入的程序。第一种程序用 string 输入操作符,而第二种则使用 getline 函数。然而,有些程序需要同时使用这两种方式:有些处理基于每行实现,而其他处理则要操纵每行中每个单词。可用 stringstreams 对象实现:1 2 3 4 56 | 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 } } |
转换 和 格式化
stringstream 对象的一个常见用法是, 需要在多种数据类型之间实现自动格式化时使用该类类型。例如,有一个数值型数据集合,要获取它们的 string 表示形式,或反之。sstream 输入和输出操作可自动地把算术类型转化为相应的 string 表示形式,反过来也可以。1 2 3 4 5 | 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的内容是以下字符:
1 | val1: 512\nval2: 1024 |
1 2 3 4 56 | // 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 |
input_istring与
string 绑定起来。在读
input_istring时,相应的值恢复为它们原来的数值型表示形式
为了读取
input_string,必须把该
string 对象分解为若干个部分。我们要的是数值型数据;为了得到它们,必须读取(和忽略)处于所需数据周围的标号。
错误处理
条件状态
IO 标准库管理一系列条件状态(condition state)条件状态(condition state)成员,用来标记给定的 IO 对象是否处于可用状态,或者碰到了哪种特定的错误。下列出了标准库定义的一组函数和标记,提供访问和操纵流状态的手段。strm::iostate | 机器相关的整型名,由各个 iostream 类定义,用于定义条件状态 |
strm::badbit | strm::iostate 类型的值,用于指出被破坏的流 |
strm::failbit | strm::iostate 类型的值,用于指出失败的 IO 操作 |
strm::eofbit | strm::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 |
每个 IO 类还定义了三个 iostate 类型的常量值,分别表示特定的位模式。这些常量值用于指出特定类型的 IO 条件,可与位操作符一起使用,以便在一次操作中检查或设置多个标志。 badbit 标志着系统级的故障,如无法恢复的读写错误。如果出现了这类错误,则该流通常就不能再继续使用了。如果出现的是可恢复的错误,如在希望获得数值型数据时输入了字符,此时则设置 failbit 标志,这种导致设置 failbit 的问题通常是可以修正的。eofbit 是在遇到文件结束符时设置的,此时同时还设置了 failbit。
流的状态由 bad、fail、eof 和 good 操作提示。如果 bad、fail 或者 eof 中的任意一个为 true,则检查流本身将显示该流处于错误状态。类似地,如果这三个条件没有一个为 true,则 good 操作将返回 true。 clear 和 setstate 操作用于改变条件成员的状态。clear 操作将条件重设为有效状态。在流的使用出现了问题并做出补救后,如果我们希望把流重设为有效状态,则可以调用 clear 操作。使用 setstate 操作可打开某个指定的条件,用于表示某个问题的发生。除了添加的标记状态,setstate
将保留其他已存在的状态变量不变。
clear 和 setstate 操作用于改变条件成员的状态。clear 操作将条件重设为有效状态。在流的使用出现了问题并做出补救后,如果我们希望把流重设为有效状态,则可以调用 clear 操作。使用 setstate 操作可打开某个指定的条件,用于表示某个问题的发生。除了添加的标记状态,setstate 将保留其他已存在的状态变量不变。
流状态的查询和控制
可以如下管理输入操作1 2 3 4 56 | 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 } |
清除流状态
考虑这样的程序,它有一个 vector 对象,包含一些要打开并读取的文件名,程序要对每个文件中存储的单词做一些处理。假设该 vector 对象命名为 files,程序也许会有如下循环:1 2 3 4 56 | // for each file in the vector while (it != files.end()) { ifstream input(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); ++it; // increment iterator to get next file } |
c_str成员。文件由构造函数打开,并假设打开成功,读取文件直到到达文件结束符或者出现其他的错误条件为止。
在这个点上,input 处于错误状态。任何读 input 的尝试都会失败。因为 input 是 while 循环的局部变量,在每次迭代中创建。这就意味着它在每次循环中都以干净的状态即 input.good() 为 true,开始使用。
如果希望避免在每次 while 循环过程中创建新流对象,可将 input 的定义移到 while 之前。这点小小的改动意味着必须更仔细地管理流的状态。如果遇到文件结束符或其他错误,将设置流的内部状态,以便之后不允许再对该流做读写操作。关闭流并不能改变流对象的内部状态。如果最后的读写操作失败了,对象的状态将保持为错误模式,直到执行 clear 操作重新恢复流的状态为止。调用 clear 后,就像重新创建了该对象一样。
如果打算重用已存在的流对象,那么 while 循环必须在每次循环进记得关闭(close)和清空(clear)文件流:
1 2 3 4 56 | 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 } |
相关文章推荐
- C++ 学习笔记 1.0 : 基本语言(变量和基本类型,标准库类型,表达式,语句,函数,标准IO库)
- (笔记)C++ Primer/第八章 标准IO库
- C++复习 08 标准IO库
- C++ Primer学习笔记:C++ 常用标准类型小结
- C++学习笔记--标准库类型(vector,iterator,bitset)(三)
- C++语法基础--标准IO库--字符串流
- C++语法基础--标准IO库--输出缓冲管理
- 标准C++复习&学习笔记
- [C++]将标准IO库应用于套接字
- C++学习笔记--标准库类型(vector,iterator,bitset)(一)
- c++学习笔记--C++Primer第4版--标准库类型vector
- (笔记)C++ Primer/第三章 标准库类型
- c++标准IO库
- [C++]将标准IO库应用于套接字
- C++标准IO库
- C++学习笔记--标准库类型(vector,iterator,bitset)(二)
- 《Inside C++ Object 》 阅读笔记(3),实践是检验真理的唯一标准
- 关注C++细节——标准IO库
- c++primer学习笔记 ( 第八章 标准IO库)
- C++学习笔记--标准输入输出