第17章 输入、输出和文件
17.1 C++输入和输出概述
可以在C++程序中使用stdio:h文件(较新的实现使用头文件cstdio)。
C++的I/O解决方案在头文件iostream(以前为iostream.h)和fstream(以前为fstream.h)中定义一组类。
17.1.1 流和缓冲区
C++程序通常在用户发送换行符时刷新输出缓冲区。
C++程序通常在用户按下回车键时刷新输入缓冲区。
17.1.2 流缓冲区和iostream文件
-
streambuf类为缓冲区提供了内存,并提供了用于填充缓冲区、访问缓冲区内容、刷新缓冲区和管理缓冲区内存的类方法:
-
ios_base类表示流的一般特征,如是否可读取、是二进制流还是文本流等;
-
ios类基于ios_base,其中包括了一个指向streambuf对象的指针成员;
-
ostream类是从ios类派生而来的,提供了输出方法;
-
istream类也是从ios类派生而来的,提供了输入方法:
-
iostream类是基于istream 和ostream类的,因此继承了输入方法和输出方法。
-
cin对象对应于标准输入流。在默认情况下,这个流被关联到标准输入设备(通常为键盘)。
-
couf对象与标准输出流相对应。在默认情况下,这个流被关联到标准输出设备(通常为显示器)。
-
cerr对象与标准错误流相对应,可用于显示错误消息。在默认情况下,这个流被关联到标准输出设备(通常为显示器)。这个流没有被缓冲,这意味着信息将被直接发送给屏幕,而不会等到缓冲区填满或新的换行符。
-
clog对象也对应着标准错误流。在默认情况下,这个流被关联到标准输出设备(通常为显示器)。这个流被缓冲。
-
wcin对象与此类似,但处理的是wchar_t类型。
17.2 使用cout进行输出
17.2.1 重载的<<运算符
ostream类都提供了operator<<()函数的定义:unsigned char、signed char、char、short、unsigned short、int、unsiged int、long unsigned、long、long long (C++11)、unsigned long long (C++11)、float、double、long double、const signed char *、const unsigned char *、const char *、void *。
对于其他类型的指针,C++将其对应于void *,并打印地址的数值表示。
17.2.2 其他ostream方法
ostream &put(char); //显示字符
basic_ostreamk<charT,traits> &write(const char_type *s, streamsize n); //第一个参数提供了要显示的字符串的地址,第二个参数指出要显示多少个字符。
17.2.3 刷新输出缓冲区
控制符flush刷新缓冲区,而控制符endl刷新缓冲区,并插入一个换行符。
调用flush(cout);来刷新cout缓冲区。
17.2.3 刷新输出缓冲区
控制符flush刷新缓冲区,而控制符endl刷新缓冲区,并插入一个换行符。
调用flush(cout);来刷新cout缓冲区。
17.2.4 用cout进行格式化
对于char值,如果它代表的是可打印字符,则将被作为一个字符显示在宽度为一个字符的字段中。
对于数值整型,将以十进制方式显示在一个刚好容纳该数字及负号的字段中。
浮点数:
- 新式:浮点类型被显示为6位,末尾的0不显示。当指数大于等于6或小于等于-5时,将使用科学计数法表示。字段宽度恰好容纳数字和负号。
- 老式:浮点类型显示为带6位小数,末尾的0不显示。当指数大于等于6或小于等于-5时,将使用科学计数法表示。字段宽度恰好容纳数字和负号。
控制整数以十进制、十六进制还是八进制显示,可以使用dec、hex和oct控制符。
可以使用cout << dec;
或dec(cout);
int width(); int width(int i);
第一种格式返回字段宽度的当前设置;第二种格式将字段宽度设置为i个空格,并返回以前的字段宽度值。只影响将显示的下一个项目。
C++永远不会截短数据。
默认右对齐。
在默认情况下,cout用空格填充字段中未被使用的部分,可以用fill()成员函数来改变填充字符。
precision()成员函数设置精度,在默认模式下,它指的是显示的总位数。在定点模式和科学模式下,精度指的是小数点后面的位数。
setf()函数:fmtflags setf(fmtflags);
,参数是一个fmtflags值,指出要设置哪一位。返回值是类型为fmtflags的数字,指出所有标记以前的设置。
- ios_base::boolalpha:输入和输出bool值,可以为true或false
- ios_base::showbase:对于输出,使用C++基数前缀(0,0x)
- ios_base::showpoint:显示末尾的小数点
- ios_base::uppercase:对于16进制输出,使用大写字母
- ios_base::showpos:E表示法在正数前面加上+
setf()函数:fmtflags setf(fmtflags, fmtflags);
,第一参数和以前一样,也是一个包含了所需设置的fimtflags值。第二参数指出要清除第一个参数中的哪些位。并返回以前的设置。
- 第二个参数为ios_base::basefield
- ios_base::dec:使用基数10
- ios_base::oct:使用基数8使
- ios_base::hex:用基数16
- 第二个参数为ios_base::floatfield
- ios_base::fixed:使用定点计数法
- ios_base::scientific:使用科学计数法
- 第二个参数为ios_base::adjustfield
- ios_base::left:使用左对齐
- ios_base::right:使用右对齐
- ios_base::internal:符号或基数前缀左对齐,值右对齐
仅当基数为10时才使用加号。
unsetf():void unsetf(fmtflags mask);
,mask是位模式。mask中所有的位都设置为1,将使得对应的位被复位。
标准控制符:
- boolalpha:setf(ios_base::boolalpha)
- noboolalpha:unset(ios_base::noboolalpha)
- showbase:setf(ios_base::showbase)
- noshowbase:unsetf(ios_base::showbase)
- showpoint:setf(ios_base::showpoint)
- noshowpoint:unsetf(ios_base::showpoint)
- showpos:setf(ios_base::showpos)
- noshowpos:unsetf(ios_base::showpos)
- uppercase:setf(ios_base::uppercase)
- nouppercase:unsetf(ios_base::uppercase)
- internal:set(ios_base::internal, ios_base::adjustfield)
- left:setf(ios_base::left, ios_base::adjustfield)
- right:setf(ios_base::right, ios_base::adjustfield)
- dec:setf(ios_base::dec, ios_base::basefield)
- hex:setf(ios_base::hex, ios_base::basefield)
- oct:setf(ios_base::oct, ios_base:: basefield)
- fixed:setf(ios_base::fixed, ios_base::floatfield)
- scientific:setf(ios_base::scientific, ios_base::floatfield)
在头文件iomanip中提供了其他一些控制符,3个最常用的控制符分别是setprecision()、setfill()、setw(),它们分别用来设置精度、填充字符和字段宽度。
17.3 使用cin进行输入
istream类(在iostream头文件中定义)都提供了operator>>()函数的定义:unsigned char &、signed char &、char &、short &、unsigned short &、int &、unsiged int &、long &、unsigned long &、long long & (C++11)、unsigned long long & (C++11)、float &、double &、long double &、signed char *、unsigned char *、char *。
可以将hex、oct和dec控制符与cin一起使用,来指定将整数输入解释为十六进制、八进制还是十进制格式。
17.3.1 cin>>如何检查输入
抽取运算符跳过空白(空格、换行符和制表符),直到遇到非空白字符。在单字符模式下,>>运算符将读取该字符,将它放置到指定的位置。在其他模式下,>>运算符将读取一个指定类型的数据。也就是说,它读取从非空白字符开始,到与目标类型不匹配的第一个字符之间的全部内容。
输入有时可能没有满足程序的期望。抽取运算符将不会修改原变量的值,并返回0。
17.3.2 流状态
cin或cout对象包含一个描述流状态的数据成员(从iosbase类那里继承的)流状态(被定义为iostate类型是一种bitmask类型)由3个ios_base元素组成:eofbit、badbit、failbit。
当cin操作到达文件末尾时,它将设置eofbit。
当cin操作末能读取到预期的字符时,它将设置failbit。
I/0失败,也可能将failbit设置为1。
在一些无法诊断的失败破坏流时,badbit元素将被设置。
- eofbit:如果到达文件尾,则设置为1
- badbit:如果流被破坏;则设置为1
- failbit:如果输入操作未能读取预期的字符或输出操作没有写入预期的字符,则设置为1
- goodbit:另一种表示0的方法
- good():如果流可以使用(所有的位都被清除),返回true
- eof():如果eofbit被设置,则返回true
- bad():如果badbit被设置,则返回true
- fail():如果eofbit或failbit被设置,则返回true
- rdstate():返回流状态
- exceptions():返回一个位掩码,指出哪些标记导致异常被引发
- exceptions(isostate ex):设置哪些状态将导致clear()引发异常
- clear(iostate s):将流状态设置为s:s的默认值为0;如果
(restate() & exceptions()) != 0
,则引发异常basic_ios::failure - setstate(iostate s):将设置与s中设置的位对应的流状态位,其他流状态位保持不变
ios_base::failure异常类是从std::exception类派生而来的。
只有在流状态良好(所有的位都被清除)的情况下,下面的测试才返回true:while(cin >> input)
设置流状态位将对后面的输入或输出关闭,直到位被清除。
17.3.3 其他istream类方法
方法get(char &)和get(void)提供不跳过空白的单字符输入功能。get(char &)返回一个指向用于调用它的istream对象的引用。get(void)返回字符,到文件尾返回EOF,返回值应该用int来接收。
函数get(char *, int, char)和getline(char*, int, char)在默认情况下读取整行而不是一个单词。
istream &get(char *, int, char);
istream &get(char *, int);
istream &getline(char, int, char);
istream &getline(char *, int);
第一个参数是用于放置输入字符串的内存单元的地址。第二个参数比要读取的最大字符数大1。第3个参数指定用作分界符的字符,只有两个参数的版本将换行符用作分界符。上述函数都在读取最大数目的字符或遇到换行符后为止。
get()将换行符留在输入流中,而gerline()抽取并丢弃输入流中的换行符。
istream &ignore(int = 1, int = EOF);
:该函数接受两个参数:一个是数字,指定要读取的最大字符数:另一个是字符,用作输入分界符。例如cin.ignore(255, '\n');
读取并丢弃接下来的255个字符或直到到达第一个换行符。
get(char *, int)和getline()在遇到文件尾时将设置eofbit,遇到流被破坏时将设置badbit。
getline(char *, int):如果没有读取任何字符(但换行符被视为读取了一个字符),则设置failbit。如果读取了最大数目的字符,且行中还有其他字符,则设置failbit
get(char *, int):如果没有读取任何字符,则设置failbit
17.3.4 其他istream方法
read()函数读取指定数目的字节,并将它们存储在指定的位置中。read()不会在输入后加上空值字符。该方法的返回类型为istream &。
peek()函数返回输入中的下一个字符,但不抽取输入流中的字符。
gcount()方法返回最后一个非格式化抽取方法读取的字符数。
putback()函数将一个字符插入到输入字符串中,被插入的字符将是下一条输入语句读取的第一个字符。其返回类型为istream &。
17.4 文件输入和输出
C++在头文件fstream(以前为fstream.h)中定义了多个新类,其中包括用于文件输入的ifstream类和用于文件输出的ofstream类。C++还定义了一个fstream类,用于同步文件I/O。这些类都是从头文件iostream中的类派生而来的。
17.4.1 简单的文件I/O
使用open()将这个对象与特定的文件关联起来fout.open(“jar.txt”);。
一个构造函数将创建对象和关联到文件:ofstream fout(“jar.txt”);。
可以像使用cout一样使用fout。
以默认方式打开文件来进行输出时,如果没有这样的文件,将创建一个新文件;如果有这样的文件,则打开文件将清空文件,输出将进入到一个空文件中。
当输入和输出流对象过期(如程序终止)时,到文件的连接将自动关闭。也可以使用close()方法来显式地关闭到文件的连接。
关闭这样的连接并不会删除流,而只是断开流到文件的连接。然而,流管理装置仍被保留。可以将流重新连接到同一个文件或另一个文件。
17.4.2 流状态检查和is_open()
试图打开一个不存在的文件进行输入时,将设置failbit位。
is_open()方法检查文件是否被打开。
17.4.4 命令行处理技术
int main(int argc, char *argv[])
argc为命令行中的参数个数,其中包括命令名本身。argv变量为一个指针,它指向一个指向char的指针。其中的指针指向命令行参数,argv[0]是一个指针,指向存储第一个命令行参数的字符串的第一个字符,依此类推。
17.4.5 文件模式
将流与文件关联时(无论是使用文件名初始化文件流对象,还是使用open()方法),都可以提供指定文件模式的第二个参数。
- ios_base::in:打开文件,以便读取
- ios_base::out:打开文件,以便写入
- ios_base::ate:打开文件,并移到文件尾
- ios_base::app:追加到文件尾,只允许将数据添加到文件尾
- ios_base::trunc:如果文件存在,则截短文件
- ios_base::binary:二进制文件
ifstream open()方法和构造函数用ios_base::in作为模式参数的默认值。
ofstream open()方法和构造函数用ios_base::out | ios_base::trunc作为默认值。
要以二进制格式存储数据,可以使用write()成员函数。
要使用文件恢复信息,请通过一个ifstream对象使用相应的read()方法。
17.4.6 随机存取
fstream类为此继承了两个方法:seekg()和seekp(),前者将输入指针移到指定的文件位置,后者将输出指针移到指定的文件位置。也可以将seekg()用于ifstream对象,将seekp()用于oftream对象。
basic_istream<charT, traits> &seekg(off_type, ios_base::seekdir);
basic_istream<charT, traits> &seekg(pos_type);
第一个原型定位到离第二个参数指定的文件位置特定距离(单位为字节)的位置;
第二个原型定位到离文件开头特定距离(单位为字节)的位置。
ios_base::beg指相对于文件开始处的偏移量。常量ios_base::cur指相对于当前位置的偏移量;常量ios_base::end指相对于文件尾的偏移量。
如果要检查文件指针的当前位置,则对于输入流,可以使用tellg()方法,对于输出流,可以使用tellp()方法。它们都返回一个表示当前位置的streampos值(以字节为单位,从文件开始处算起)。
char *tmpnam(char *pszName);
创建一个临时文件名,将它放在pszName指向的C风格字符串中。常量L_tmpnam和TMP_MAX(二者都是在cstdio 中定义的)限制了文件名包含的字符数以及在确保当前目录中不生成重复文件名的情况下tmpnam()可被调用的最多次数。
17.5 内核格式化
C++库还提供了sstream族,它们使用相同的接口提供程序和string对象之间的I/O。
头文件sstream定义了一个从ostream类派生而来的ostringstream类(还有一个基于wostream的wostringstream类,这个类用于宽字符集)。
ostringstream类有一个名为str()的成员函数,该函数返回一个被初始化为缓冲区内容的字符串对象。
istringstream类许使用istream方法族读取istringstream对象中的数据,istringstream对象可以使用string对象进行初始化。istringstream instr(facts);