C++的输入/输出由标准库提供,支持对文件、控制窗口和string对象的读写。
8.1 面向对象的程序库
IO类型在三个独立的头文件中定义,iostream定义读写控制窗口的类型,fstream定义读写已命名文件的类型,sstream所定义的类型用于读写存储在内存中string对象。
如果函数有基类类型的引用形参时,可以给函数传递其派生类型的对象。即对istream&进行操作的函数,也可以使用ifstream或者istringstream对象来调用。
标准库定义了一组相关类型,支持wchar_t类型,每个类都加上“w”前缀,与char类型的版本区分。如wiostream、wfstream、wstringstream等。
IO对象不可复制或赋值
1)流对象不能复制,故不能存储在vector等容器中;
2)形参或返回类型不能为流类型。如要传递或返回IO对象,必须指定为该对象的指针或引用。
8.2 条件状态
条件状态
badbit标志着系统级的故障,无法恢复;failbit是IO错误,通常可以修正;eofbit是在遇到文件结束符时设置的,此时同时设置了failbit
bad、fail、eof中任意一个为true,则流处于错误状态;若三者都不为true,则good操作返回true。
clear操作将条件重设为有效状态。
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 }
条件状态的访问
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
多种状态的处理
使用按位或在一次调用中生成“传递两个或更多状态位”的值
// sets both the badbit and the failbit is.setstate(ifstream::badbit | ifstream::failbit);
生成一个值,对应于badbit和failbit的位都打开了,将这两个位都设置为1,该值的其他位都为0
8.3 输出缓冲区的管理
每个IO对象都管理一个缓冲区,用于存储程序读写的数据。下面几种情况会导致缓冲区的内容被刷新
1)程序正常结束
2)在一些不确定的时候,缓冲区已满,写入新数据前被刷新
3)用操纵符显式地刷新缓冲区
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
4)每次操作执行完后,用unitbuf操纵符设置流的内部状态,从而清空缓冲区
cout << unitbuf << "first" << " second" << nounitbuf; //is equivalent to writing cout << "first" << flush << " second" << flush;
unitbuf在每次执行完写操作后都刷新流,nonunitbuf操作符将流恢复为使用正常的、由系统管理的缓冲区刷新方式。
5)将输出流与输入流关联,在读输入流时将刷新其关联的输出缓冲区
标准库默认将cout与cin绑定在一起
tie函数可用istream或ostream调用,使用一个指向ostream对象的指针形参。如果在调用tie函数时传递实参0,则打破该流上已存在的捆绑。
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
8.4 文件的输入和输出
fstream除了继承下来的行为外,还定义了两个自己的新操作,open,close
流对象的两种初始化形式
// construct an ifstream and bind it to the file named ifile ifstream infile(ifile.c_str()); ifstream infile; // unbound input file stream infile.open("in"); // open file named "in" in the current directory
由于历史原因,IO标准库使用C风格字符串而不是C++ string类型的字符串作为文件名。
检查文件打开是否成功
// check that the open succeeded if (!infile) { cerr << "error: unable to open input file: " << ifile << endl; return -1; }
将文件流与新文件重新捆绑(文件流改变关联文件时,需先关闭当前的文件流)
ifstream infile("in"); // opens file named "in" for reading infile.close(); // closes "in" infile.open("next"); // opens file named "next" for reading
清除文件流的状态
重用已存在的流对象,while循环必须在每次循环时记得关闭和清空文件流,因成功打开文件并读取直到文件结束或出现其他错误为止,流对象处于错误状态,任何读取流对象的尝试都会失败。
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 }
文件模式
in 读打开,ifstream默认方式
out 写打开,ofstream默认方式,文件会被清空
app 末尾写打开
ate 打开定位到末尾
trunc 清空打开
binary 二进制形式打开
一个打开并检查输入文件的程序
// 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 }
8.5 字符串流
stringstream对象的使用
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提供的转换
int val1 = 512, val2 = 1024; ostringstream format_message; // ok: converts values to a string representation format_message << "val1: " << val1 << " " << "val2: " << val2 << " ";
// 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
使用输入操作符读string时,空白符将会忽略。