Chapter17 输入、输出和文件
本章内容包括:
- C++角度的输入和输出
- iostream类系列
- 重定向
- ostream类方法
- 格式化输出
- istream类方法
- 流状态
- 文件I/O
- 使用ifstream类从文件输出
- 使用ofstream类输出到文件
- 使用fstream类进行文件输入和输出
- 命令行处理
- 二进制文件
- 随机文件访问
- 内核格式化
17.1 C++输入和输出概述
17.1.1 流和缓冲区
对键盘输入进行缓冲可以让用户在将输入传输给程序之前返回并更正。C++程序通常在用户按下回车键时刷新缓冲区。对于屏幕输出,C++程序通常在用户发送换行符时刷新输出缓冲区。程序也可能在其它情况下刷新输入。
17.1.2流、缓冲区和isotream文件
iostream文件中包含一些专门设计用来实现、管理流和缓冲区的类。
- streambuf类为缓冲区提供了内存,并提供了用于填充缓冲区、访问缓冲区内容、刷新缓冲
区和管理缓冲区内存的类方法; - ios_base类表示流的一般特征,如是否可读取、是二进制流还是文本流等;
- ios类基于ios_base,其中包括了一个指向streambuf对象的指针成员;
- ostream类是从ios类派生而来的,提供了输出方法;
- istream类也是从ios类派生而来的,提供了输入方法;
- isotream类是基于istream和ostream类的,因此继承了输入方法和输出方法。C++的isotream类库管理了很多细节,例如,在程序中包含iostream文件将自动创建8个流对象(比如cin,cout)。cout对象凭借streambuf对象的帮助,管理着流中的字节流。
17.1.3 重定向
17.2 使用cout进行输出
ostream重载了<<运算符,使之能够识别C++所有的基本类型。又因为插入运算符的所有化身的返回类型都是ostream&,它就能够进行拼接输出。
其它ostream方法
put()
原型:ostream & put(char);
write()
原型:basic_ostream<charT,traits>& write(const char_type* s,streamsize n);
第一个参数提供了要显示的字符串的地址,第二个参数指出要显示多少个字符。
刷新输出缓冲区
对于屏幕输出来说,填充缓冲区的重要性要低得多。所以多数C++实现都会在输入即将发生时刷新缓冲区。控制符flush刷新缓冲区,而控制符endl刷新缓冲区,并插入一个换行符。这两个控制符的使用方式与变量名相同。
用cout进行格式化
修改显示时使用的计数系统
Ios_base类储存了描述格式状态的信息。要控制整数以十进制、十六进制还是八进制显示,可以使用dec、hex和oct控制符。例如,下面的函数调用将cout对象的计数系统格式状态设置为十六进制:
hex(cout);
或cout<<hex;
调整字段宽度
可以使用width成员函数将长度不同的数字放到宽度相同的字段中,该方法的原型为:
Int width();
Int width(int i);
第一种格式返回字段宽度的当前设置;第二种格式将字段宽度设置为i个空格,并返回以前的字段宽度值。这使得能够保存以前的值,以便以后恢复宽度值时使用。width()
方法只影响将显示的下一个项目,然后字段宽度将恢复为默认值。
填充字符
可以用fill()成员函数来改变填充字符。例如,下面的函数调用将填充字符改为星号。
Cout.fill(‘*’);
与字段宽度不同的是,新的填充字符将一直有效,知道更改它为止。
设置浮点数的显示精度
函数:precision()
e.g: cout.precision(2);
将精度设置为2,设置将一直有效。
打印末尾的0和小数点
Ios_base类提供了一个setf()
函数(用于set标记),能够控制多种格式化特征。例如,下面的函数调用使cout显示末尾小数点:
Cout.setf(ios_base::showpoint);
使用默认的浮点格式时,上述语句还将导致末尾的0被显示出来。
再谈setf()
修改将一直有效
常量 | 含义 |
---|---|
ios_base::boolalpha | 输入和输出bool值,可以为true或false |
ios_base::showbase | 对于输出,使用c++基数前缀(0,0x) |
ios_base::showpoint | 显示末尾的小数点 |
ios_base::uppercase | 对于16进制输出,使用大写字母,E表示法 |
ios_base::showpos | 在正数前面加上+ |
第二个setf()原型接受两个参数,并返回以前的设置:
Fmtflages setf(fmtflags, fmtflags);
第二个参数 | 第一个参数 | 含义 |
---|---|---|
ios_base::basefield | ios_base::dec | 使用基数10 |
ios_base::oct | 使用基数8 | |
ios_base::hex | 使用基数16 | |
ios_base::floatfield | ios_base::scientific | 使用科学计数法 |
ios_base_adjustfield | ios_base::left | 使用左对齐 |
ios_base::right | 使用右对齐 | |
ios_base::internal | 符号或基数前缀左对齐,值右对齐 |
Ios_base::fmtflags old=cout.setf(ios::left,ios::adjustfield);
要恢复以前的设置,可以这样做:
Cout.setf(old,ios::adjustfield);
当然,也可以通过调用unsetf()函数来进行恢复
标准控制符
C++提供了一些控制符,可以通过cout直接来调用,这样就不用使用setf()函数了。
e.g:
Cout<<left<<fixed;
头文件iomanip
C++在头文件iomanip中提供了其它一些控制符,它们能够提供前面讨论过的服务,但表示起来更方便。3个最常用的控制符分别是setprecision()、setfill()和setw(),它们分别用来设置精度、填充字符和字段宽度。
17.3 使用cin进行输入
cin重载了>>运算符,使之能够识别大部分基本类型。cin也能使用一些控制符。如cin>>hex,可以将整数解释为十六进制数。
17.3.1 cin>>如何检查输入
Cin将跳过空白(空格、换行符和制表符),直到遇到非空白字符。
17.3.2 流状态
成员 | 描述 |
---|---|
eofbit | 如果到达文件尾,则设置为1 |
badbit | 如果流被破坏,则设置为1;例如,文件读取错误 |
failbit | 如果输入操作未能读取预期的字符或输出操作没有写入预期的字符,则设置为1 |
goodbit | 另一种表示0的方法 |
good() | 如果流可以使用(所有的位都被清除),则返回true |
eof() | 如果eofbit被设置,则返回true |
bad() | 如果badbit被设置,则返回true |
fail() | 如果badbit或failbit被设置,则返回true |
rdstate() | 返回流状态 |
exceptions() | 返回一个位掩码,指出哪些标记导致异常被引发 |
exceptions(iostate ex) | 设置哪些状态将导致char()引发异常;例如,如果ex是eofbit,则如果eofbit被设置,clear()将引发异常 |
clear(iostates) | 将流状态设置为s;s的默认值为0(goodbit);如果(restate()&exceptions是())!=0,则引发异常basic_ios::failure |
setstate(iostates) | 调用clear(rdstate()|s)。这将设置与s中设置的位对应的流状态位,其它流状态位保持不变。 |
17.3.3 其它istream方法
- 方法
get(char&)
和get(void)
提供不跳过空白的单字符输入功能。 - 函数
get(char*,int,char)
和getline(char*,int,char)
在默认情况下读取整行而不是一个单词 - 函数
ignore()
,接受两个参数:一个是数字,指定要读取的最大字符数;另一个是字符,用作输入分界符。例如,下面的函数调用读取并丢弃接下来的255个字符或知道达到第一个换行符。
e.g:
Cin.ignore(255,‘\n’);
它们被称为非格式化输入函数(unformatted input functions),因为它们只是读取字符输入,而不会跳过空白,也不进行数据转换。
17.3.4 其它istream方法
read()
读取指定数目的字节,并将它们储存在指定的位置中
peek()
返回输入中的下一个字符,但不抽取输入流中的字符
gcount()
返回最后一个非格式化抽取方法读取的字符数
putback()
将一个字符串插入到输入字符串中,被插入的字符将是下一条输入语句读取的第
一个字符
17.4 文件输入和输出
17.4.1 简单的文件I/O
要让程序写入文件,必须这样做:
- 创建一个ofstream对象来管理输出流
- 将该对象与特定文件关联起来
- 以使用cout的方式使用该对象,唯一的区别是输出将进入文件,而不是屏幕
读取文件的要求与写入文件相似: - 创建一个ifstream对象来管理输入流
- 将该对象与特定的文件关联起来
- 以使用cin的方式使用该对象
e.g:
#include <iostream>
#include <fstream>
#include <string>
int main(void)
{
using namespace std;
string filename;
cout << "Enter name for new file: ";
cin >> filename;
ofstream fout(filename.c_str());
fout << "For your eyes only!\n";
cout << "Enter your secret number: ";
float secret;
cin >> secret;
fout << "Your secret number is " << secret << endl;
fout.close();
ifstream fin(filename.c_str());
cout << "Here are the contents of " << filename << ":\n";
char ch;
while (fin.get(ch))
cout << ch;
fin.close();
}
17.4.5 文件模式
表17.8
C++模式 | C模式 | 含义 |
---|---|---|
Ios_base::in | “r” | 打开以读取 |
Ios_base::out | “w” | 等价于ios_base::out|ios_base::trunc |
Ios_base::out|ios_base::trunc | “w” | 打开以写入,如果已经存在,则截断文件打开以写入,只追加 |
ios_base::out|ios_base::app | “a” | 打开以读写,在文件允许的位置写入 |
ios_base::in|ios_base::out | “r+” | 打开以读写,如果文件已经存在,则首先截断文件 |
c++mode|ios_base::binary | “cmodeb” | 以C++mode(或相应的cmode)和二进制模式打开;例如,ios_base::in|ios_base::binary 成为“rb” |
c++mode|ios_base::ate | “comode” | 以指定的模式打开,并移到文件尾。C使用一个独立的函数调用,而不是模式编码。例如,ios_base::in|ios_base::ate 被转换为“r”模式和C函数调用fseek(file,0,SEEK_END) |
17.4.6 随机存取
随机存取指的是直接移动(不是依次移动)到文件的任何位置。
17.5 内核格式化
读取string对象中的格式化信息或将格式化信息写入string对象中被称为内核格式化(incore formating)
- 必须包含头文件fstream
- 头文件fstream定义了一个用处理输出的ofstream类
- 需要声明一个或多个ofsteam变量(对象)
- 必须指明名称空间std
- 需要将ofstream对象与文件关联起来。为此,方法之一是使用open()方法
- 使用完文件后,用close()将其关闭
- 写入文本文件
创建对象:
ofstream outFile
ofstream fout
打开文件:
Outfile.open("fish.txt");
Char filename[50];
Cin>>filename;
Fout.open(filename);
使用对象:
Double wt=125.8;
Outfile<<wt;
Char line[81]="Objects are closer than they appear.";
Fout<<line<<endl;
使用某些方法:
Cout.precision(2);
2.读取文本文件
创建对象:
ifstream inFile
ifstream fin
打开文件:
infile.open("fish.txt");
Char filename[50];
Cin>>filename;
Fin.open(filename);
使用对象:
Double wt;
Outfile>>wt;
Char line[81];
Fin.getline(line,81);
使用某些方法:
Is_open()
–检查文件是否被成功打开
exit(EXIT_FAILURE)
–定义于cstdilb
Good()
–在没有发生任何错误时返回True
Eof()
Bad()
Fail()