题记:本系列学习笔记(C++ Primer学习笔记)主要目的是讨论一些容易被大家忽略或者容易形成错误认识的内容。只适合于有了一定的C++基础的读者(至少学完一本C++教程)。
流介绍
<iostream> | 包含istream、ostream、iostream这三个类。其中,iostream由istream和ostream派生而来。 |
<fstream> | 包含ifstream、ofstream、fstream这三个类。其中,ifstream由istream派生,ofstream由ostream派生,fstream由iostream派生。 |
<sstream> | 包含istringstream、ostringstream、stringstream这三个类。其中,istringstream由istream派生,ostringstream由ostream派生,stringstream由iostream派生。 |
注意:标准库I/O对象不允许拷贝或赋值。 |
C++标准库的流类完整关系图如下:(注意:我发现一个问题,下面的图在打开网页时有时显示模糊,显示的图比实际的小,有两种方法:一是多刷新几次就能刷出来;二是另外显示该图片^_^)
其中,typedef basic_ios<char, char_traits<char> > ios;
typedef basic_istream<char, char_traits<char> > istream;
typedef basic_ostream<char, char_traits<char> > ostream;
typedef basic_iostream<char, char_traits<char> > iostream;
typedef basic_istringstream<char> istringstream;
typedef basic_ostringstream<char> ostringstream;
typedef basic_stringstream<char> stringstream;
ios_base::badbit | 该标志位表示该I/O流发生了严重错误(常指物理破坏,没法修正) |
ios_base::eofbit | 该标志位表示该I/O流已经到达文件尾 |
ios_base::failbit | 该标志位表示该I/O流出现了失败的I/O操作 |
ios_base::goodbit | 该标志位表示该I/O流状态正常 |
注意:各标志位可以用|运算符连接起来;为了方便,可以用ios代替ios_base来使用这些标志位(标志位是ios_base的静态常量成员),后面内容中出现的ios_base也可以这样。
ios::bad() | 如果流的badbit标志位被设置,则返回true;否则返回false |
ios::eof() | 如果流的eofbit标志位被设置,则返回true;否则返回false。如果eofbit被设置,那么failbit也将被设置。 |
ios::fail() | 如果流的failbit标志位被设置,则返回true;否则返回false |
ios::good() | 如果流的goodbit标志位被设置,则返回true;否则返回false。仅当bad(),eof(),fail()都返回 false时,good()才返回true。 |
除了可以直接调用流的这些成员函数进行流状态检测外,很多时候我们还可以把流对象直接用于布尔表达式中进行测试。例如:
int i;
while (cin >> i)
cout << i << endl;
在这种情况下,流到void*类型的转换operator void*()被隐式调用,然后再隐式转换为bool型。
为什么流类不直接定义一个到bool类型的隐式转换呢?这是因为这样做可能会引起错误。因为bool型可以隐式转换到int型,从而使流类对象可以隐式转换到int型,这样极容易引用错误。例如,原本打算输入“cin >> i”,但实际上却输入了“cin > i”,漏掉了一个“>”,如果有流对象到bool的隐式转换,那么“cin > i”语法上就是正确的,相当于最终两个int型的比较。显然,我们不希望这种情况下发生。另外,由于历史原因,bool型在最先的C++中是不存在的,所以转换到了void*类型。
设置/获取流状态:
ios_base::clear(state) | 将流s标志位设置为state,默认参数为goodbit |
ios_base::setstate(state) | 为流s添加指定的标志位state,原来的标志位仍然存在,相当于调用clear(state | rdstate()) |
ios_base::rdstate() | 返回流s的当前标志位 |
1) 程序正常结束。作为main返回工作的一部分,将清空所有输出缓冲区;
2) 在一些不确定的时候,缓冲区可能已经满了。在这种情况下,缓冲区将会在写下一个值之前刷新;
3) 用操作符(manipulator)显式地刷新缓冲区,例如 endl,flush等;
4) 在每次输出操作执行后,用unitbuf操纵符设置流的内部状态,从而清空缓冲区;
5) 可将输出流与输入流关联(tie) 起来。在这种情况下,在读输入流时将刷新其关联的输出流缓冲区。
endl操纵符 | 输出一个换行符,并刷新缓冲区 |
ends操纵符 | 输出一个空字符null,然后刷新缓冲区 |
flush操纵符 | 直接刷新缓冲区,不添加任何字符 |
unitbuf操纵符 | 它在每次执行完写操作后都刷新缓冲区 |
cout<<unitbuf<<"first"<<" second"<<nounitbuf;
等价于:
cout<<"first"<<flush<<" second"<<flush;
其中,nounitbuf操纵符将流恢复为使用正常的、由系统管理的缓冲区刷新方式。
basic_ostream<E, T> *tie() const;
basic_ostream<E, T> *tie(basic_ostream<E, T> *str);
tie函数可以由istream或ostream对象调用,使用一个指向ostream对象的指针形参。第一个函数返回上次存储的被关联的ostream对象指针,第二个函数设置新的关联对象,并返回上次关联的对象的指针。如果第二个函数的参数为0,则断开两个对象之间的关联。例如:
cin.tie(&cout); // tie cin and cout
ostream* old_tie = cin.tie();
cin.tie(0); // break tie between cin and cout
cin.tie(&err); // a new tie
cin.tie(old_tie); // restablish tie between cin and cout
文件模式
ios_base::in | 读模式 |
ios_base::out | 写模式 |
ios_base::app | 追加模式 |
ios_base::ate | 打开文件后立即定位到文件尾 |
ios_base::trunc | 打开文件时清空文件内容(如果有的话) |
ios_base::binary | 以二进制模式打开文件 |
上面的标志位被声明为类ios_base的静态常量成员。其中,out、truc、app模式只能用于与ofstreamt和fstream对象关联的文件;in模式只能用于与ifstreamt和fstream对象关联的文件;ate和binary模式可以用于所有文件。
如果以binary模式打开文件,则文件流将以字节序列处理文件内容,不会对内容作任何解释;否则,默认用文本模式打开文件,不同的系统可能会对文件内容作一些解释转换。比如,在非UNIX系统如Windows系统中,换行符/n会被解释成回车换行/r/n到文件系统,/r/n会被解释成/n到内存。而在UNIX系统中,二进制模式和文本模式是没有区别的。
out | 打开文件进行写操作,删除文件中已有数据;如果文件不存在,则创建文件。 |
app | 打开文件进行写操作,在文件尾追加数据;若文件是与ofstream流关联,那么,当文件不存在时创建文件;若是与fstream流关联,那么,当文件不存在时操作失败。 |
out | app | 追加数据;如果文件不存在,则创建文件。 |
out | trunc | 与out模式相同 |
in | 打开文件进行读操作 |
in | out | 打开文件进行读、写操作,并定位于文件形头处 |
in | out | trunc | 打开文件进行读、写操作,并且删除文件中已有数据 |
basic_string<E, T, A> str() const;
void str(basic_string<E, T, A>& x);
用来实现格式化数据输入输出,相当于C语言中的fscanf和fprintf函数的功能。例如:
|
流的格式化I/O
boolalpha | 将真和假显示为字符串 |
*noboolalpha | 将真和假显示为1,0 |
showbase | 产生数的基数前缀 |
*noshowbase | 不产生数的基数前缀 |
showpoint | 总是显示小数噗 |
*noshowpoint | 有小数部分才显示小数点 |
showpos | 显示非负数中的+(0也显示+) |
*noshowpos | 不显示非负数中的+ |
uppercase | 在十六进制中打印0X,科学记数法中打印E |
*nouppercase | 在十六进制中打印0x,科学记数法中打印e |
*dec | 用十进制显示 |
hex | 用十六进制显示 |
oct | 用八进制显示 |
left | 在对齐,在值的右边增加填充字符 |
*right | 右对齐,在值的左边增加填充字符 |
internal | 两边对齐,在值和符号之间填充字符 |
fixed | 用小数形式显示浮点数 |
scientific | 用科学记数法显示浮点数 |
flush | 刷新ostream缓冲区 |
ends | 插入空字符,然后刷新ostream缓冲区 |
endl | 插入换行符,然后刷新ostream缓冲区 |
unitbuf | 在每个输出操作之后刷新缓冲区 |
*nounitbuf | 恢复常规缓冲区刷新 |
*skipws | 为输入操作符>>跳过空白字符(空格、制表位、换行) |
noskipws | 不为输入操作符跳过空白 |
ws | “吃掉”空白 |
setfill(char ch) | 设置ch为空白填充字符 |
setprecision(int n) | 将浮点精度设置为n |
setw(int w) | 读写w个字符的值 |
setbase(int b) | 按基数b输出整数,n取值为8、10、16;若为其它值,则基数为10 |
setiosflags(ios_base::fmtflags n) | 相当于调用setf(n) |
resetiosflags(ios_base::fmtflags n) | 清除由n代表的格式化标志 |
默认情况下,精度指定数字的总位数(小数点之前和之后),默认为6位。使用fixed或者scientific之后,精度指小数点之后的位数。
ios_base::fmtflags flags( ios_base::fmtflags fmtfl); // 设置流的格式状态
ios_base::fmtflags setf( ios_base::fmtflags fmtfl, fmtflags mask);
void unsetf( ios_base::fmtflags mask); // 清除指定标志位
流的未格式化的输入输出操作
is.get(ch) | 将istream is的下一个字节放入字符ch中,返回is |
os.put(ch) | 将字节ch放入ostream os中,返回os |
is.get() | 返回is的下一字节作为一个int值 |
is.putback(ch) | 将字符ch放回is,返回is |
is.unget() | 将is退回一个字节,返回is |
is.peek() | 将下一字节作为int值返回但不移出它(不移动流缓冲区指针) |
is.get(buf, size, delim),其函数原型为: basic_istream& get(E *s, streamsize n, E delim = '/n'); | 从is中读入size个字节并将它们存储到buf所指向的空间中,返回is。当读操作遇到delim字符、或者文件结束符、或者已经读入了size个字节,那么读操作结束。如果遇到delim,它将被留在输入流中。 |
is.getline(buf, size, delim),其函数原型为:
basic_istream& getline(E *s, streamsize n, E delim = '/n'); | 与上面的get行为相似,区别是读取并丢弃delim。 |
is.read(buf, size) | 读取size个字节到buf中,返回is |
is.gcount() | 返回最后一个未格式化读操作从流is中读到的字节数 |
os.write(buf, size) | 将size个字节从数组buf写到os,返回os |
is.ignore(size, delim) | 读并忽略size个字符,直到遇到delim,但不包括delim。size的默认参数为1,delim默认参数为文件结束符。 |
举例:当输入缓冲区发生错误时,需要清空缓冲区时,可以这样做:
if (!cin) {
cin.clear();
cin.ignore(numeric_limits<int>::max(), '/n');
}
另外,需要注意的是使用低级I/O操作容易出错,提倡使用标准库的高级抽象。例如,返回int值的I/O操作就是一个很好的例子。
流的随机访问
I/O流提供了两个成员函数来实现随机访问:定位函数(seek)和查询函数(tell)。如下表所示:
seekg | 重新定位输入流中的读指针 |
tellg | 返回输入流中读指针的当前位置 |
seekp | 重新定位输出流中的写指针 |
tellp | 返回输出流中写指针的当前位置 |
basic_istream& seekg(off_type off, ios_base::seek_dir way);
basic_ostream& seekp(off_type off, ios_base::seek_dir way);
pos_type tellp();
流类和异常
iostate exceptions(iostate except);
如果文中有错误或遗漏之处,敬请指出,谢谢!
参考文献:
[2] Thinking in C++(Volume Two, Edition 2)
[3] International Standard:ISO/IEC 14882:1998