第七章 C++的 I/O流类
库
7.1 C++为何建立自己的输入输出系统
C++除了完全支持 C 语言的输入输出系统外, 还定义了一套面向对
象的输入输出系统 。 为什么 C ++还要建立自己的输入输出系统呢?
C 语言的输入输出系统不支持用户自定义的对象, 如,
struct my_struct{
int i;
float f;
char *str;
}s;
对此结构类型,在 C语言中下面的语句是不能接受的,
printf(“%my_struct”,s);
因为 printf( )函数只能识别系统预定义的类型,而没有办法对新
的数据类型进行扩充,。用 C++的输入输出系统,就可以通过重载
,<<”和,>>”运算符来解以上问题。 C++的类机制允它建立一
个可扩展的输入输出系统,它可以通过修改和扩展来加入用户自
定义类型及相应操作。
1
7.2 C++的流及流类库
7.2.1 C++的流
输入输出是一种数据传递操作,它可以看作字符序列在主机与外
部介质 之间的流动。流 (stream)为从源 (或生产者 )到目的 (或消
费者 )的数据流的引用。流具有方向性,与输入设备 (如键盘 )相
联系的流称为输入流;与输出设备 (如屏幕 )相联系的流称为输出
流;与输入输出设备 (如磁盘 )相联系的流称为输入输出流。
C++中包 含几个预定义的流,
标准输入流 cin 与标准输入设备相关联
标准输出流 cout 与标准输出设备相关联
非缓冲型标准出错流 cerr 与标准错误输出设备相关联 (非缓冲方式 )
缓冲型的标准出错流 clog 与标准错误输出设备相关联 (缓冲方式 )
在缺省情况下,指定的标准输出设备是显示终端,标准输入设备
是键盘。
在任何情况下(有时用户 把标准输出设备定向为其它设备),指
定的标准错误输出设备总是显示终端。
cerr<<“The average cannot be computed.\n”;
2
7.2.2 流类库
C++流类库是用继承方法建立起来的一个输入输出类库。它具有
两个平行的基类,streambuf 类,ios 类。所有其它的流类都是
从它们直接或间接地派生出来的。使用 C++的流类库,程序中可
能应包含的头文件,
iostream.h strstream.h fstream.h iomanip.h
1,streambuf 类
streambuf
filebuf strstreambuf conbuf
派生
3
2,ios 类
ios
fstreambase strstreambase ostream istream
istrstream ofstream ostrstream constream ifstream
fstream istream_withassign ostream_withassign
iostream_withassign
strstream
iostream
4
7.3输入输出的格式控制
7.3.1 用 ios 类的成员函数进行格式控制
ios类中有几个成员函数可以用来对输入输出进行格式控制。
主要控制 状态标志字, 域 宽, 填充字符 及 输出精度 。
1,状态标志字
状态标志存放在数据成员 long x_flags 中。 ios 类 public 中定义
了一个枚举,它的每个成员可以分别定义状标志字的一个位,每
一位都称为一个状标志位。
5
,这个枚举 定义 如下,
enum{ 0000 0000 0000 0001
skipws =0x0001,跳过输入中的空白,可用于输入
left =0x0002,左对齐输出,可用于输出
right =0x0004,右对齐输出,可用于输出
internal =0x0008,在符号位和基指示符后填入字符,可用于输出
dec =0x0010,转换基数为十进制,可用于输入或输出
oct =0x0020,转换基数为八进制,可用于输入或输出
hex =0x0040,转换基数为十六进制,可用于输入或输出
showbase =0x0080,在输出时显示基指示符,可于输入或输出
showpoint =0x0100,在输出时显示小数点,可用于输出
uppercase =0x0200,十六进制输出时,表示制式的和表示数值的
字符一律为大写,可用于输出
showpos =0x0400,正整数前显示” +”符号,可用于输出
scientific =0x0800,用科学表示法显示浮点数,可用于输出
fixed =0x1000,用定点形式显示浮点数,可用于输出
unitbuf =0x2000,在输出操作后立即刷新所有流,可用于输出
stdio =0x4000,在输出操作后刷新 stdout 和 stderr,可用于输
出 };
6
2
x_flags取以上枚举值的并存,即 x_flags上的每一位 (长整数为 16
位 )中的 0 1相当于一个枚举值。例如 x_flags 中放 0x0011 或
17即为 0000 0000 0001 0001 相当于 0x0001 与 0x0010 之
并 skipws(0x0001) 0000 0000 0000 0001
dec (0x0010) + 0000 0000 0001 0000
x_flags (0x0011) 0000 0000 0001 0001
7
2,ios类中用于控制输入输出格式的成员函数
函数原型 功能
long ios::setf(long flags); 设置状态标志 flags
long ios::unsetf(long flags); 清除 状态标志,并返回前标志
long ios::flags(); 测试状态标志
long ios::flags(long flags); 设置标志 flags,并返回前标志
int ios::width(); 返回当前的宽度设置值
int ios::width(int w); 设置域宽 w,返回以前的设置
int ios::precision(int p); 设置小数位数 p返回以前的小数位数
char ios::fill(); 返回当前的填充字符
char ios::fill(char ch); 设置填充字符 ch返回当前的填充字符
8
(1)设置状态标志
设置状态标志,即是将某一状态标志位置,1”,可使用 setf()函
数
其一般的调用格式为,流对象,setf(ios::状态标志 );
例 7.1
#include<iostream.h>
main()
{ istream cin; //对象 cin可不定义
ostream cout; //对象 cout可不定义
cin.setf(ios::shipws); //键盘输入时跳过输入空白
cout.setf(ios::left); //设置输出左对齐
cout.setf(ios::showpos | ios::sciengific);//中间用运算符, |”分
隔
cout<<567<<,” <<567.89<<endl;
}
设置 showpos使得每个正数前添加,+”号,设置 sciengific使 浮点
数按科学表示法
(指数形式 )进行显示,输出结果,
+567 +567.89e02
注意:要设置多项标志时,中间用或运算符, 分隔。
9
(2)清除状态标志
清除某一状态标志,即是将某一状态标志位置” 0”,可使用
unseft()函数,使用时的调用格式与 setf()相同。
(3)取状态标志
取一个状态标志,可使用 flags()函数。 flags()函数有不带参数
与带 参数两 种形式,
ling ios,:flags(); 用于返回当前的状态 标志字;
ling ios,:flags (liog flag); 返回当前的状态 标志字后,再
者将状态标志字设置成 flag(参数 )。
flags(liog flag)函数与 setf(liog flags)函数的差别在于,setf()函数
是在原有的基础上追加设定的,而 flags()函数是用 新设定覆盖
以前的状态标志字。
例 7.2 几个成员 函 数的使用 方法
#include <iostream.h>
void showflags(long f) //输出状态标志字函数
{ long i;
for(i=0x8000;i;i=i>>1)//用右移方法使 i中的值为” 1”的位不断右
移
if (i&f) cout<<”1”; //判断 f中的某一位是否为” 1”
else cout <<”0”;
cout<<endl;
}
10
接 1 例 7.2
void main()
{ long f;
cout.setf(ios::unitbuf|ios::skipws);
f=cout.flags(); //取当前状态标志字
showflags(f); //显示状态标志字
cout.setf(ios::showpos|ios::scientific); //追加状态 标志位
f=cout.flags();
showflags(f);
cout.unsetf(ios::scientific); //从状态标志字中去掉
sciengific
f=cut.flags();
showflags(f);
f=cout.flags(ios::oct); //重新设置状态标志字
showflags(f); //显示设置前的状态标志字
f=cout.flags(); //取设置 后的状态标志字
showflags(f); //显示设置后的状态标志字
}
11
程序运行结果为 ;
0010000000000001 (1)
0010110000000001 (2)
0010010000000001 (3)
0000000000100000 (4)
0000000000100000 (5)
(4)设置域宽
域宽主要用来控制输出,在 ios 类中域 宽存放在数据成员 int
x_width中,设置域宽的成员函数有两个,其一般格式为,
int ios,:width(); 返回当的域宽值
int ios,:width(int w); 设置域宽,并返回原来的域 宽
(5)设置输出的精度
精度用来控制浮点数的输出显示精度,在 ios类中用数据成员 int
x_precision来存放精度,设置精度的成员函数的一般格式为,
int ios::precision(int p); 重新设置浮点数所需小数的位数,并返
回设置前的位数
(6)填充字符
填充字符的作用是:当输出值不满域宽时用填充字符来填充,
缺省情况下填充字符为空格,所以在使用填充字符函数时,必须
与 width()函数相配合,否则就没有意义,在 ios类中用数据成员
x_fill来存放填充字符。填充字符的成员函数有两个,其一般形
式为,char ios,:fill(); 返回当的填充字符
char ios,:fill(char ch); 用 ch重新设置填充字符,并返
回设置前的填充字符 。
12
例 7.3
#include <iostream.h>
void main()
{ cout<<”x_width=”<<cout.width()<<endl;
cout<<”x_fill=”<<cout.fill()<<endl;
cout<<”x_precision=”<<cout.precision()<<endl;
cout<<123<<”,<<123.45678<<endl;
cout<<”---------------------------------------\n”;
cout<<”****x_width=10,x_fill=,x_precision=4****\n”;
cout.width(10);
cout.precision(4);
cout<<123 <<”,<<123.45678<<”,<<234.567<<endl;
cout <<”x_width=”<<cout.width()<<endl;
cout<<”x_fill=”<<cout.fill()<<endl;
cout<<”x_precision=”<<cout.precision()<<endl;
cout<<”---------------------------------------\n”;
cout<<”****x_width=10,x_fill=&,x_precidion=4****\n”;
13
接 1 例 7.3
cout.fill(?&?);
cout.width(10);
cout<<123<<”,<<123.45678<<endl;
cout.setf(ios::left);
cout.width(10);
cout<<123<<”,<<123.45678<<endl;
cout<<”x_width=”<<cout.width()<<endl;
cout<<”x_fill=”<<cout.fill()<<endl;
cout<<”x_precision=“<<cout.precision()<<endl;
}
14
程序运行结果如下,
x_width=0
x_fill=
x_precision=0
123 123.45678
-------------------------------------
****x_width=10,x_fill=,x_preciosion=4****
123 123.4568 234.567
x_width=0
x_fill=
x_precision=4
-------------------------------------
****x_width=10,x_fill=&,x_precision=4****
&&&&&&&123 123.4568
123&&&&&&& 123.4568
x_width=0
x_fill=&
x_precision=4
例 7.4 利用控制输入输出格式的成员函数建立对齐的数字表
#include<iostream..h>
#include<math.h>
void main()
{ double x;
cout,precision(4);
cout<<” x sqrt(x) x^2\n\n”;
for(x=1.0;x<=20.0;x++)
{ cout.width(8);
cout<<x<<’ ‘;
cout.width(8);
cout<<sqrt(x)<<’ ‘;
cout.width(8);
cout<<x*x<<’\n’;
}
}
这程序建立了如下的表
x sqrt(x) x^2
1 1 1
2 1.4142 4
3 1.7321 9
4 2 16
5 2.2361 25
6 2.4495 36
7 2.6458 49
8 2.8284 64
9 3 81
10 3.1623 100
11 3.3166 121
12 3.4641 144
13 3.6056 169
14 3.7417 196
15 3.873 225
16 4 256
17 4.1231 289
18 4.2426 324
19 4.3598 361
20 4.4721 400
15
7.3.2使用操作符进行输入输出格式控制
C++提供了另一种进行 I/O格式控制的方法,这一方法使用了一种
称为操纵符 (也称为操纵函数 )的特殊函数。在很多情况下,使用
操纵符进行格式化控制比用 ios格式标志和成员函数要方便。
1,C++预定义的操纵符
C++预定义的操纵符是以一个流引用作为其参数,并返回同一流
的引用,因此它可以嵌入到输入输出操作的链中。操纵符可用来
改变域宽,使整型数以八进制或十六进制形式输入或输出,并设
置填充字符等。许多操纵符的功能类似于上面介绍的 ios类成员
函数的功能。 C++提供的预定义操纵符如下,
( 1 ) dec 以十进制形式输入或输出整型数,可用于输入或输出 。
( 2 ) hex 以十六进制形式输入或输出整型数,可用于输入或输出。
( 3 ) oct 以八进制形式输入或输出整型数,可用于输入或输出。
( 4 ) ws 用于在输入时跳过开头的空白符,仅用于输入。
( 5 ) endl 插入一个换行符并刷新输出流,仅用于输出。
( 6 ) ends 插入一个空字符,通常用来结束一个字符串,仅用于输出。
( 7 ) flush 刷新一个输出流,仅用于输出。
( 8 ) setbase( int n )把转换基数设置为 n(n的值为 0,8,10,或 16),n的缺
省值为” 0”,即以十进制形式输出。
16
( 9 ) resetiosflags (long f ) 关闭由参数 f 指定的格式标志,可用于
输入或输出。
( 10 ) setiosflags (long f ) 设置由参数 f 指定的格式标志,可用于
输入或输出。
( 11 ) setfill (int c ) c 为填充字符,缺省时为空格,可用于输入
或输出。
( 12 ) setprecision (int n )设置数据小数部分的位数,缺省时小数的
位数为 6,可用输入输出。
( 13 ) setw (int n) 设置域宽为 n,可用于输入或输出。
17
操纵符 setiosflags( )和 resetiosflags ( )中所用的格式标志
格式标志名 含 义
ios,,left 输出数据按域宽左对齐输出
ios,,right 输出数据按域宽右对齐输出
ios,:scientific 使用科学计数法表示浮点数
ios,:fixed 使用定点形式表示浮点数
ios,:dec 转换基数为十进制形式
ios,:hex 转换基数为十六进制形式
ios,:oct 转换基数为八进制形式
ios,:uppercase 十六进制形式和科学计数法输出时,表示数值的字
符 一律为大写
ios,:showbase 输出带有一个表示制式的字符 (如” X”表示十六
进
制,”O”表示八进制 )
ios,:showpos 在正数前添加一个” +”号
ios,:showpoint 浮点输出时必须带有一个小数点
2.操纵符的使用
操纵符分为带参数的操纵符和不带参数的操纵符,通常,不带参
数的操纵符在 iostream.h 文件中定义,带参数的操纵符在
iomanip.h 文件中定义。
18
例 7,5 使用操纵符的输入输出格式控制
#include <iostream.h>
#include <iomanip.h>
void main ( )
{ cout <<setw (10)<<123<<567<<endl; //①
cout<<123<<setiosflags(ios::scientific)<<setw(20)<<123.45
6789<<endl; //②
cout <<123<<setw(10)<<hex<<123<<endl; //③
cout <<123<<setw(10)<<oct(8)<<123<<endl; //④
cout <<123<<setw(10)<<setbase(0)<<123<<endl; //⑤
cout<<resetiosflags(ios::scientific)<<setprecision(4)<<123.
456789<<endl;⑥
cout<<setiosflags(ios::left)<<setfill(?#?)<<setw(8)<<123<<en
dl; // ⑦
cout<<resetiosflags(ios::left)<<setfill(?$?)<<setw(8)<<456<<
endl; // ⑧
}
19
程序运行结果为,
1234567 ①
123 1.234567e+02 ②
123 7b ③
7b 173 ④
173 123 ⑤
123.4568 ⑥
123##### ⑦
$$$$$456 ⑧
例 7.6 二次方表与二次方根表的程序的程序的另一个版本
#include<iostream.h>
#include<iomanip.h>
#include<math.h>
void main ( )
{ double x;
cout<<setprecision (4);
cout <<, x sqrt (x) x^2\n\n”;
for (x=1.;x<=20.0;x++);
{
cout <<setw(8)<<x<<? ?;
cout <<setw(8)<<sqrt(x)<<? ?;
cout <<setw(8)<<x*x<<?n?;
}
}
20
3,用户自定义的操纵符
C++除了提供此同预定义的操纵符外,也允许用户自定义操纵符,
把程序中频繁使用的输入输出操作合并成一个操纵符。
若为输出流定义操纵符函数,则定义形式如下,
ostream &manip_name(ostream &stream)
{
//自定义代码
return stream;
}
若为输入流定义操纵符函数,则定义形式如下,
istream &manip_name(istream &stream)
{
//自定义代码
return stream;
}
21
例 7.7 自定义的操纵符
#include <iostream.h>
#include<iomanip.h>
ostream &output 1(ostream &stream)
{ stream.setf(ios::left);
stream<<setw(10)<<hex<<setfill(?&?);
return stream;
}
void main( )
{ cout<<123<<endl;
cout<<output 1<<123<<endl;
}
程序运行结果如下,
123
7b&&&&&&&&
22
例 7.8
#include<iostream.h>
#include<iomanip.h>
istream &inputl (istream &in)
{ cin>>hex;
cout<<,Enter number using hex format:”;
return in;
}
void main ( )
{ int i;
cin>>inputl>>i;
cout <<endl;
}
以上程序中定义了一个操纵符函数 input1,该函数要求输入一
个十六进制数。
程序运行后,屏幕上显示,
Enter number using hex format,
提示用户输入一个十六进制数。
23
7.4用户自定义类型的输入输出
用户自定义的类型数据的输入或输出,可以通过重载运算符
,>>”和, <<”来实现,使,>>”和, <<”可以直接用来输入或输
出类的对象。
7.4.1 重载输出运算符, <<”
定义输出运算符” <<”重载函数的一般格式如下,
ostream &operator <<(ostream &stream,class_name obj )
{
//操作代码
return stream;
}
函数中的一个参数是对 ostream 对象的引用,它可以是其它任何合
法的标示符,但必须与 return后面的标示符相同。第二个参数接
受将被输出的对象,其中 class_name是类名,obj为该类的对象
名。
24
例 7.9输出运算符, <<”重载的例子
#include <iostream.h>
class coord{
public,
int x,y;
coord ( ) {x=0; y=0;}
coord ( int I,int j) {x=I; y=j;}
};
ostream &operator <<(ostream &stream,coord ob)
{ stream <<ob.x<<,,”<<ob.y<<endl;
return stream;
}
void main ( )
{ coord a ( 5,6),b (10,34);
cout <<a<<b;
}
程序运行结果如下,
5,6
10,34
25
例 7.10 把重载输出运算符定义为类的友元函数,这样就可以访问
类的私有成员
#include <iostream.h>
class coord{
int x,y;
public,
coord ( ) {x=0; y=0;}
coord ( int i,int j) {x=i; y=j;}
friend ostream &operator<<(ostream &stream,coord ob);
};
ostream &operator<<(ostream &stream,coord ob)
{ stream<<ob.x <<”,”<<ob.y<<endl;
return stream;
}
void main ( )
{ coord a ( 5,6),b (10,34);
cout <<a<<b;
}
26
程序运行结果如下,
5,6
10,34
7.4.2 重载输入运算符, >>”
只是要把 ostream换成 istream,把” <<”用” >>”代替,第二个
参数是一个引用。
定义输入运算符” >>”重载函数的一般格式如下,
istream &operator >>(istream &stream,class_name &obj )
{
//操作代码
return stream;
}
例 7.11重载输入运算符, >>”
#include<iostream.h>
class three_d{
int x,y,z;
public,
three_d(int a,int b,int c) {x=a; y=b; z=c;}
friend ostream &operator<<(ostream &output,three_d ob);
friend istream &operator>>(istream &input,three_d &ob);
};
27
接 1 例 7.11
ostream &operator<<(ostream &output,three_d ob);
{ output<<ob.x<<,,”;
output<<ob.y<<,,”;
output<<ob.z<<endl;
return output ;
}
istream &operator>>(istream &input,three_d &ob);
{ cout<<”Enter x,y,z value:”;
input>>ob.x;
input>>ob.y;
input>>ob.z;
return input;
}
28
接 2 例 7.11
void main ( )
{ three_d obj (10,20,30); //定义类 three_d的对象 obj
cout<<obj; //输出对象 obj的成员值
cin>>obj; //输出对象 obj的各成员值,将原值覆盖
cout<<obj; //输出对象 obj的成员值 (新值 )
}
程序运行结果如下,
10,20,30
Enter x,y,z value,40 50 60
40,50,60
29
7.5文件的输入输出
C++把文件看作字符序列, 即文件是由一个字符数据顺序组成的 。
根据数据的组织形式, 文件可分为文本文件和二进制文件 。 文本
文件又称 ASCII文件, 它的每个字节存放一个 ASCII代码, 代表
一个字符 。 二进制文件则是把内存中的数据, 按其在内存中的存
储形式原样写到磁盘上存放 。 文件是一个字符流或二进制流, 它
把数据看作是一连串的字符, 而不考虑记录的界限, 它对文件的
存取以字符为单位进行 。 我们把这种文件称为流式文件 。 在 C++中,
要进行文件的输入输出, 必须首先 创建一个流, 然后将这个流与
文件相关联, 即打开文件, 此时才能进行读写操作, 完成后再关
闭这个文件 。 这是 C++中进行文件输入输出的基本过程 。
7.5.1 文件的打开与关闭
1,文件的打开
为了执行文件的输入输出, C++提供了三个流类 。
名 称 基 类 功 能
ofstream 由 ostream 派生 用于文件的输出
ifstream 由 istream 派生 用于文件的输入
fstream 由 iostream 派生 用于文件的输入或输出
30
建立流的过程就是定义流类的对象,例如,
ifstream in;
ofstream out;
fstream both;
它们分别定义了输入流对象 in,输出流对象 out,输入输出流对象
both。
Open( )函数是上述三个流类的成员函数, 其原型为,
void open(const unsigned char *,int mode,
int access=filebuf::openprot);
打开语句,
流 类 的 对 象, open(const unsigned char *, int mode, int
access=filebuf::openprot);
其中第一个参数是用来传递 文件名 的;第二个参数 mode的值决
定文件将 如何 被 打开, 它必须取下面的值中的一个; access的值
决定文件的 访问方式 。
31
ios::app 使输出追加到文件尾部
ios::ate 查找文件尾
ios::in 打开一个文件进行读操作
ios::nocreate 文件不存在时,导致 open( )失败
ios::noreplace 若文件存在,则 open( )失败
ios::out 打开一个文件进行写操作
ios::trunc 使同名文件被删除
ios::binary 文件以二进制方式打开,缺省时为文本文件
访问方式
0 普通文件
1 只读文件
2 隐含文件
4 系统文件
8 备份文件
32
打开文件的步骤,
(1) 定义一个类的对象, 例如,ofstream out;
(2) 使用 open( )函数打开文件 。 例如,out.open(”test”,ios::out, 0);
文件只有在打开后, 才能对文件进行读写操作 。 由于某些原因,
可能打开失败, 因此使用文件之前必须进行执行检测, 以确认打
开一个文件是否成功 。 可以使用类似下面的方法进行检测,
if( ! mystream)
{ cout<<”cannot open file! \n”;
//错误处理代码
}
在实际编程时, 打开一个文件的最常见的形式为,
ofstream out (”test”) ; 用 if (!out) 检测是否打开成功
它相当于语句,
ofstream out;
out.open(”test”);
2,文件的关闭
关闭文件可使用 close( )函数完成, close函数也是流类中的成员函
数, 它不带参数, 没有返回值, 例如,out.close ( );
33
7.5.2 文件的读写
在含有文件操作的程序中, 必须有如下的预处理命令,
# include <fstream.h>
1,文本文件的读写
文件打开后, 文本文件的读写使用运算符, <<”与, >>”,只是必
须用与文件相连接的流代替 cin和 cout。
例 7.12 把一个整数、一个浮点数和一个字符串写到磁盘文件 test
中。
# include <fstream.h>
int main ()
{ ofstream fout(” test”);
if (! fout)
{ cout<<”cannot open output file \n”;
return 1;
}
fout<<10<<”,<<123.456<<” \”This is a test file.\”\n”;
fout.close();
return 0;
}用“写字板”可以看到 test文件的内容,10 123.456,this is a
test file”
34
例 7.13 先建立一个输出文件,向它写入数据,然后关闭文件,再按
输入模式打开它,并读取信息。
# include <fstream.h>
void main ()
{ ofstream fout(” test2”);
if(! fout)
{ cout<<”cannot open output file,\n”; return 1; }
fout<<” Hello! \n”;
fout<<100<<? ?<<hex<<100<<endl;
fout.close ();
ifstream fin(” test2”);
if(! fin)
{ cout<<”cannot open input file,\n”; return 1; }
char str[80];
int I;
fin>>str>>i;
cout<<str<<”,<<i<<endl;
fin.close();
}
程序建立一个输出文件 test2,并向它写入数据,关闭输出文件 test2
。再将文件 test2按输入模式打开,并将字符串,hello! \n”读给字符
数组 str,将整数 100读给整型变量 i。最后在屏幕上显示出 str和 I的值
,如下所示,
Hello! 100
35
例 7.14 从键盘读入字符串,并将它们写进磁盘 文件 。当用户输入
空白字符时,程序停止。
# include <fstream.h>
# include <stdio.h>
void main( int argc,char *argv[])
{ if( argc!=2)
{ cout<<”Usage,write <filename>\n”; return 1; }
ofstream outf (argv[1]);
if (! outf)
{ cout<<”cannot open output file,\n”; return 1; }
char str[60];
cout<<”Write strings to disk,RETURN to stop\n”;
do
{ cout<<”:”;
gets(str);
outf<<str<<endl;
}while (*str);
outf.close ();
}
这个例子采用了命令行参数的形式,输入以下命令行,
<可执行文件名 > <磁盘 文件名 > ↙
程序运行情况如下,
Write strings to disk,RETURN to stop
,abcdefg (从键盘输入一个字符串)
,↙ (输入空行,程序停止)
程序运行后,在磁盘上生成一个名为 <磁盘 文件名 >的文件,
其内容,abcdefg
36
2,二进制文件的读写
任何文件, 无论它包含格式化的文本还是包含原始数据, 都能以
文本方式或二进制方式打开 。 文本文件是字符流, 而二进制文件
是字节流 。
(1) 用 get( )函数和 put( )函数读写二进制文件
get( )是输入流类 istream中定义的成员函数, 它可以从与流对象连
接的文件中读出数据, 每次读出一个字节 (字符 )。 put( )是输出流
类 ostream中的成员函数, 它可以向与流对象连接的文件中写入
数据, 每次写入一个字节 ( 字符 ) 。
get( )函数有许多格式, 其最一般的使用版本的原型如下,
istream &get(unsigned char &ch);
get( )函数从相关流中只读一个字符, 并把该值放如引用 ch&中 。
put( )函数的原型如下,
ostream &put(char ch);
put( )函数将字符 ch写入流中 。
37
例 7.15 实现任意类型的文件拷贝。
# include <iostream.h>
# include <fstream.h>
void main (int argc,char *argv[ ])
{ char ch;
if (argc!=3)
{ cout<<”Bad commond !\n”; return 0; }
ifstream inf (argv[1]);
if (! inf)
{ cout<<”Cannot open souce file.”; return 1; }
ofstream outf (argv[2]);
if (! outf)
{ cout<<”Cannot open target file.”; return 1; }
while (inf)
{ inf.get (ch);
outf.put (ch);
}
inf.close ( );
outf.close ( );
}
假定本程序的文件名为 mvc7_15.cpp,经编译、连接
后生成的可执行文件名为 mvc7_15.exe,则在 DOS下输入
以下命令行,
D> mvc7_15 file1.exe file2.exe
该程序的执行结果,是把文件 file1.exe中的信息拷贝
到文件 file2.exe中
38
eof( )函数的使用
二进制文件的处理过程与文本文件的处理过程基本相同, 但在判
断文件是否结束时有所区别 。 在文本文件中, 遇到文件结束符,
get函数返回一个文件结束标志 EOF,该标志的值为 -1。 但在处
理二进制文件时, 读入某一字节中的二进制数的值可能是 -1,这
与 EOF的值相同 。 这样就有可能出现读入的有用数据被处理成
,文件结束, 的情况 。 为了解决这个问题, c++提供了一个成员
函数 eof,用来判断文件是否真的结束, 其原型为,
int eof( ) ;
当到达文件末尾时, 它返回一个非零值, 否则返回零 。 当输入文
件是键盘时, 其结束符是 ctrl_z,eof( ) 函数返回的值为真 。
39
例 7.16在屏幕上显示任何给定文件的内容 。
# include <iostream.h>
# include <fstream.h>
main (int argc,char *argv[ ])
{ char ch;
if (argc!=2)
{ cout<<”Usage,PR<filename>\n”; return 1; }
ifstream in (argv[1]);
if (! in)
{ cout<<”Cannot open file”; return 1; }
while (! in.eof ( ))
{ in.get (ch);
cout<<ch;
}
return 0;
}
假定本程序的文件名为 mvc7_16.cpp,经编译
、连接后生成的可执行文件名为 mvc7_16.exe,则
在 DOS下输入以下命令行,
D> mvc7_16 file1
该程序的执行结果,是把文件 file1 中的信息在
屏幕上显示 。
40
(2) 用 read( )函数和 write( )函数读写二进制文件
有时需要读写一组数据 (如一个结构变量的值 ),为此 C++提供了
两个函数 read( )和 write( ),用来读写一个数据块 。
① read( )函数,
函数原型,istream &read (unsigned char *buf,int num);
read( )是类 istream中的成员函数, 其功能为:从相应的流中读
取 num个字节 (字符 ),并把它们放入指针 buf所指的缓冲区中 。
调用格式,read(缓冲区首址, 读入的字节数 );
注意:, 缓冲区首址, 的数据类型为 unsigned char*,当输入其它
类型数据时, 必须进行类型转换, 例如,int array[ ]={50,60,
70};定义了一个整型数组 array,
read((unsigned char *)&array,sizeof(array);
对整型数组 array,为了把数据从文件读入到它中, 必须在 read( )
函数中把它转换为 unsigned char *类型 。 由 sizeof( )函数确定
要读入的字节数 。
41
② write( )函数,
函数原型,ostream &write(const unsigned char *buf,int
num);
write( )是流类 ostream的成员函数,利用该函数,可以把 buf所
指的缓冲区 num个字节写到相应的流上。参数的含义及调用注意事
项与 read()函数类似。
注意:如果在 num个字节被读出之前就达到了文件尾,则 read( )只
是停止执行,此时缓冲区包含这些字符。我们可以用另一个成员函
数 gcount( )统计出有多少字符被读出。 gcount( )的原型如下,
int gcount( );它返回所读取的字节数 。
42
例 7.17 用 write( )函数向文件 test中写入双精度数与字符串。
# include <iostream.h>
# include <fstream.h>
# include <string.h>
main( )
{ ofstream outf(”test”);
if (! out)
{ cout<<”cannot open output file,\n”; return 1; }
double num=100.45;
char str [ ]=”This is a test”;
out.write ((char * )&num,sizeof (double ));
out.write (str,strlen (str));
out.close ( );
return 0;
} 程序执行后,屏幕上不显示任何信息,但程序已将双精度
100.45和字符串,this is a test” 以二进制形式写入文件 test中。
用下面的程序 可以读取文件 test中的数据,并在屏幕上显示出来,
以验证前面的程序操作。
43
例 7.18 用 read( ) 函数读取例 7.17中程序所建立的文件。
# include <iostream.h>
# include <fstream.h>
# include <string.h>
main( )
{ ifstream in(, test”) ;
if (! in) { cout<<”cannot open input file,\n”; return 1; }
double num;
char str [80];
in.read ((char * )&num,sizeof (double));
in.read (str,14);
cout<<num<<? ?<<str;
in.close ( );
return 0;
}
程序运行结果为,
100.45 This is a test
44
3,文件的随机读写
前面介绍的文件操作都是按一定顺序进行读写的, 因此称为顺序文
件 。
C++在类 istream及类 ostream中定义了几个与在输出流中随机移动
文件指针的成员函数, 则可以在流内随机移动文件指针, 从而可
以对文件的任意数据进行随机读写 。 用函数 seekg( )和 seekp( )进
行随机访问 。 函数 seekg( )用于输入文件, 函数 seekp( )用于输出
文件 。
45
它们的 函数原型 如下,
istream &seekg (streamoff offset,seek_dir origin);
ostream &seekp (streamoff offset,seek_dir origin);
其中,参数 origin表示文件指针的 起始位置, offset表示相对于这个
起始位置的 位移量 。 seek_dir是一个系统定义的枚举名,origin是枚
举变量。 origin的取值有以下三种情况,
? ios::beg 从文件头开始,把文件指针移动由 offset指定的距离。
offset的值为正数
? ios::cur 从当前位置开始,把文件指针向前 (后 )移动由 offset指定
的距离。 offset 的值为正或负数,正数时向前移,负数时向后移。
? ios::end 从文件尾开始,把文件指针移动由 offset指定的距离,
offset的值为负数进行文件的随机读写时,可以用以下函数 测试 文
件指针的 当前位置,
streampos tellg( );用于输入文件。 streampos tellp( );用于输出
文件。
streampos是在 iostream.h中定义的类型,实际上是 long型的。
46
例 7.19 演示 seekp( )函数。它修改文件中的指定字符。执行时在命
令行指定文件名,其后跟想修改的字符的位置,然后再跟新的字符。
# include <iostream.h>
# include <fstream.h>
# include <stdlib.h>
int main (int argc,char *argv[ ])
{ if (argc!=4)
{ cout<<”Usage,CHANGE<filename> <byte> <char>\n”;
return 1; }
fstream out (argv[1],ios::in|ios::out);
if (! out)
{ cout<<”Cannot open file”<<argv[1]<<”\n”; return 1; }
out.seekp (atoi (argv[2]),ios::beg);
out.put ( *argv[3]);
out.close ( );
return 0;
}
假定上述程序的文件名为 mvc7_19.cpp,经编译、连接生成
的可执行文件名为 mvc7_19.exe。需要修改的文件名为 file
,file的内容为,AAAAAAAAAA1234567890
执行下述命令,
D> mvc7_19 file 5 #
程序运行结果为,
AAAAA#AAAA1234567890
47
例 7.20 使用 seekg( )函数,它从指定的位置开始显示文件内容。
# include <iostream.h>
#include <fstream.h>
# include <stdlib.h>
void main (int argc,char * argv[ ])
{ char ch;
if (argc!=3 )
{ cout<<”Usage,LOCATE<filename><loc>\n”; return 1; }
ifstream inf (argv[1] );
if (! inf)
{ cout<<”Cannot open input file,\n”; return 1; }
inf.seekg (atoi (argv[2]),ios::beg);
while (! inf.eof ( ))
{ inf.get (ch);
cout<<ch;
}
inf.close ( );
}
假定程序的执行文件文件名为 mvc7_20.exe,要显示文件
名为 file,希望从第 5个位置开始显示, 则执行的命令行,
D>mvc7_20 file 5 <cr>
程序运行结果为,
#AAAA1234567890
42
48
7.6 应 用 举 例
例 7.21 重载运算符, <<”和, >>”,使用户能直接输入和输出复数 。
# include<iostream.h>
class complex{
double real,imag;
public,
complex(double r,double i) { real=r; imag=i;}
complex( ) { real=0; imag=0;}
friend complex operator+(complex,complex);
friend ostream &operator<<(ostream&,complex&);
friend istream &operator>>(istream&,complex&);
};
complex operator+(complex a,complex b)//定义重载运算符, +”
{ complex temp;
temp.real=a.real+b.real;
temp.imag=a.imag+b.imag;
return temp;
}
49
接 1 例 7.21
ostream &operator<<(ostream &output,complex &obj) //定
义重载运算符,<<”
{ output<<obj,real;
if(obj.imag>0) output<<”+”;
if(obj.imag!=0) output<<obj.imag<<“i”;
return output;
}
istream &operator>>(istream &input,complex &obj) //定义
重载运算符,>>”
{ cout<<”Input the real,imag of the complex:\n”;
input>>obj.real;
input>>obj.imag;
return input;
}
50
void main()
{ complex c1(2.4,4.6),c2,c3;
cout<<”the value of c1 is:”<<c1<<endl;
cin>>c2;
cout<<”the value of c2 is:”<<c2<<endl;
c3=c1+c2;
cout<<”the value of c3 is:”<<c3<<endl;
}
程序运行后屏幕显示为,
the value of c1 is,2.4+4.6i
Input the real,imag of the complelx,
若此时输入,
3.7 2.5
屏幕上又显示出,
the value of c2 is 3.7+2.5i
the value of c3 is 6.1+7.1i
51
例 7.22 下面的程序建立了类 triangle,用来存储直角三角形的宽与
高。这个类的重载输出运算符函数在屏幕上显示三角形。
# include<iostream.h>
class triangle{
int height,base;
public,
triangle (int h,int b) { height=h; base=b; }
friend ostream &operator << (ostream &stream,triangle
ob);
};
ostream &operator<<(ostream &stream,triangle ob)
{ int I,,j,h,k;
i=j=ob.base-1;
for (h=ob.height-1;h;h--)
{ for (k=i;k;k--)
stream<<? ?;
stream<<?*?;
52
接 1 例 7.22
if (j!=i)
{ for(k=j-i-1;k;k--)
stream<<? ?;
stream<<?*?;
}
i--;
stream<<endl;
}
for (k=0;k<ob.base;k++) stream<<?*?;
stream<<endl;
return stream;
}
void main ()
{ triangle t1(5,5),t2(10,10),t3(12,12);
cout<<t1<<endl;
cout<<t2<<endl;
cout<<t3<<endl;
}
53
库
7.1 C++为何建立自己的输入输出系统
C++除了完全支持 C 语言的输入输出系统外, 还定义了一套面向对
象的输入输出系统 。 为什么 C ++还要建立自己的输入输出系统呢?
C 语言的输入输出系统不支持用户自定义的对象, 如,
struct my_struct{
int i;
float f;
char *str;
}s;
对此结构类型,在 C语言中下面的语句是不能接受的,
printf(“%my_struct”,s);
因为 printf( )函数只能识别系统预定义的类型,而没有办法对新
的数据类型进行扩充,。用 C++的输入输出系统,就可以通过重载
,<<”和,>>”运算符来解以上问题。 C++的类机制允它建立一
个可扩展的输入输出系统,它可以通过修改和扩展来加入用户自
定义类型及相应操作。
1
7.2 C++的流及流类库
7.2.1 C++的流
输入输出是一种数据传递操作,它可以看作字符序列在主机与外
部介质 之间的流动。流 (stream)为从源 (或生产者 )到目的 (或消
费者 )的数据流的引用。流具有方向性,与输入设备 (如键盘 )相
联系的流称为输入流;与输出设备 (如屏幕 )相联系的流称为输出
流;与输入输出设备 (如磁盘 )相联系的流称为输入输出流。
C++中包 含几个预定义的流,
标准输入流 cin 与标准输入设备相关联
标准输出流 cout 与标准输出设备相关联
非缓冲型标准出错流 cerr 与标准错误输出设备相关联 (非缓冲方式 )
缓冲型的标准出错流 clog 与标准错误输出设备相关联 (缓冲方式 )
在缺省情况下,指定的标准输出设备是显示终端,标准输入设备
是键盘。
在任何情况下(有时用户 把标准输出设备定向为其它设备),指
定的标准错误输出设备总是显示终端。
cerr<<“The average cannot be computed.\n”;
2
7.2.2 流类库
C++流类库是用继承方法建立起来的一个输入输出类库。它具有
两个平行的基类,streambuf 类,ios 类。所有其它的流类都是
从它们直接或间接地派生出来的。使用 C++的流类库,程序中可
能应包含的头文件,
iostream.h strstream.h fstream.h iomanip.h
1,streambuf 类
streambuf
filebuf strstreambuf conbuf
派生
3
2,ios 类
ios
fstreambase strstreambase ostream istream
istrstream ofstream ostrstream constream ifstream
fstream istream_withassign ostream_withassign
iostream_withassign
strstream
iostream
4
7.3输入输出的格式控制
7.3.1 用 ios 类的成员函数进行格式控制
ios类中有几个成员函数可以用来对输入输出进行格式控制。
主要控制 状态标志字, 域 宽, 填充字符 及 输出精度 。
1,状态标志字
状态标志存放在数据成员 long x_flags 中。 ios 类 public 中定义
了一个枚举,它的每个成员可以分别定义状标志字的一个位,每
一位都称为一个状标志位。
5
,这个枚举 定义 如下,
enum{ 0000 0000 0000 0001
skipws =0x0001,跳过输入中的空白,可用于输入
left =0x0002,左对齐输出,可用于输出
right =0x0004,右对齐输出,可用于输出
internal =0x0008,在符号位和基指示符后填入字符,可用于输出
dec =0x0010,转换基数为十进制,可用于输入或输出
oct =0x0020,转换基数为八进制,可用于输入或输出
hex =0x0040,转换基数为十六进制,可用于输入或输出
showbase =0x0080,在输出时显示基指示符,可于输入或输出
showpoint =0x0100,在输出时显示小数点,可用于输出
uppercase =0x0200,十六进制输出时,表示制式的和表示数值的
字符一律为大写,可用于输出
showpos =0x0400,正整数前显示” +”符号,可用于输出
scientific =0x0800,用科学表示法显示浮点数,可用于输出
fixed =0x1000,用定点形式显示浮点数,可用于输出
unitbuf =0x2000,在输出操作后立即刷新所有流,可用于输出
stdio =0x4000,在输出操作后刷新 stdout 和 stderr,可用于输
出 };
6
2
x_flags取以上枚举值的并存,即 x_flags上的每一位 (长整数为 16
位 )中的 0 1相当于一个枚举值。例如 x_flags 中放 0x0011 或
17即为 0000 0000 0001 0001 相当于 0x0001 与 0x0010 之
并 skipws(0x0001) 0000 0000 0000 0001
dec (0x0010) + 0000 0000 0001 0000
x_flags (0x0011) 0000 0000 0001 0001
7
2,ios类中用于控制输入输出格式的成员函数
函数原型 功能
long ios::setf(long flags); 设置状态标志 flags
long ios::unsetf(long flags); 清除 状态标志,并返回前标志
long ios::flags(); 测试状态标志
long ios::flags(long flags); 设置标志 flags,并返回前标志
int ios::width(); 返回当前的宽度设置值
int ios::width(int w); 设置域宽 w,返回以前的设置
int ios::precision(int p); 设置小数位数 p返回以前的小数位数
char ios::fill(); 返回当前的填充字符
char ios::fill(char ch); 设置填充字符 ch返回当前的填充字符
8
(1)设置状态标志
设置状态标志,即是将某一状态标志位置,1”,可使用 setf()函
数
其一般的调用格式为,流对象,setf(ios::状态标志 );
例 7.1
#include<iostream.h>
main()
{ istream cin; //对象 cin可不定义
ostream cout; //对象 cout可不定义
cin.setf(ios::shipws); //键盘输入时跳过输入空白
cout.setf(ios::left); //设置输出左对齐
cout.setf(ios::showpos | ios::sciengific);//中间用运算符, |”分
隔
cout<<567<<,” <<567.89<<endl;
}
设置 showpos使得每个正数前添加,+”号,设置 sciengific使 浮点
数按科学表示法
(指数形式 )进行显示,输出结果,
+567 +567.89e02
注意:要设置多项标志时,中间用或运算符, 分隔。
9
(2)清除状态标志
清除某一状态标志,即是将某一状态标志位置” 0”,可使用
unseft()函数,使用时的调用格式与 setf()相同。
(3)取状态标志
取一个状态标志,可使用 flags()函数。 flags()函数有不带参数
与带 参数两 种形式,
ling ios,:flags(); 用于返回当前的状态 标志字;
ling ios,:flags (liog flag); 返回当前的状态 标志字后,再
者将状态标志字设置成 flag(参数 )。
flags(liog flag)函数与 setf(liog flags)函数的差别在于,setf()函数
是在原有的基础上追加设定的,而 flags()函数是用 新设定覆盖
以前的状态标志字。
例 7.2 几个成员 函 数的使用 方法
#include <iostream.h>
void showflags(long f) //输出状态标志字函数
{ long i;
for(i=0x8000;i;i=i>>1)//用右移方法使 i中的值为” 1”的位不断右
移
if (i&f) cout<<”1”; //判断 f中的某一位是否为” 1”
else cout <<”0”;
cout<<endl;
}
10
接 1 例 7.2
void main()
{ long f;
cout.setf(ios::unitbuf|ios::skipws);
f=cout.flags(); //取当前状态标志字
showflags(f); //显示状态标志字
cout.setf(ios::showpos|ios::scientific); //追加状态 标志位
f=cout.flags();
showflags(f);
cout.unsetf(ios::scientific); //从状态标志字中去掉
sciengific
f=cut.flags();
showflags(f);
f=cout.flags(ios::oct); //重新设置状态标志字
showflags(f); //显示设置前的状态标志字
f=cout.flags(); //取设置 后的状态标志字
showflags(f); //显示设置后的状态标志字
}
11
程序运行结果为 ;
0010000000000001 (1)
0010110000000001 (2)
0010010000000001 (3)
0000000000100000 (4)
0000000000100000 (5)
(4)设置域宽
域宽主要用来控制输出,在 ios 类中域 宽存放在数据成员 int
x_width中,设置域宽的成员函数有两个,其一般格式为,
int ios,:width(); 返回当的域宽值
int ios,:width(int w); 设置域宽,并返回原来的域 宽
(5)设置输出的精度
精度用来控制浮点数的输出显示精度,在 ios类中用数据成员 int
x_precision来存放精度,设置精度的成员函数的一般格式为,
int ios::precision(int p); 重新设置浮点数所需小数的位数,并返
回设置前的位数
(6)填充字符
填充字符的作用是:当输出值不满域宽时用填充字符来填充,
缺省情况下填充字符为空格,所以在使用填充字符函数时,必须
与 width()函数相配合,否则就没有意义,在 ios类中用数据成员
x_fill来存放填充字符。填充字符的成员函数有两个,其一般形
式为,char ios,:fill(); 返回当的填充字符
char ios,:fill(char ch); 用 ch重新设置填充字符,并返
回设置前的填充字符 。
12
例 7.3
#include <iostream.h>
void main()
{ cout<<”x_width=”<<cout.width()<<endl;
cout<<”x_fill=”<<cout.fill()<<endl;
cout<<”x_precision=”<<cout.precision()<<endl;
cout<<123<<”,<<123.45678<<endl;
cout<<”---------------------------------------\n”;
cout<<”****x_width=10,x_fill=,x_precision=4****\n”;
cout.width(10);
cout.precision(4);
cout<<123 <<”,<<123.45678<<”,<<234.567<<endl;
cout <<”x_width=”<<cout.width()<<endl;
cout<<”x_fill=”<<cout.fill()<<endl;
cout<<”x_precision=”<<cout.precision()<<endl;
cout<<”---------------------------------------\n”;
cout<<”****x_width=10,x_fill=&,x_precidion=4****\n”;
13
接 1 例 7.3
cout.fill(?&?);
cout.width(10);
cout<<123<<”,<<123.45678<<endl;
cout.setf(ios::left);
cout.width(10);
cout<<123<<”,<<123.45678<<endl;
cout<<”x_width=”<<cout.width()<<endl;
cout<<”x_fill=”<<cout.fill()<<endl;
cout<<”x_precision=“<<cout.precision()<<endl;
}
14
程序运行结果如下,
x_width=0
x_fill=
x_precision=0
123 123.45678
-------------------------------------
****x_width=10,x_fill=,x_preciosion=4****
123 123.4568 234.567
x_width=0
x_fill=
x_precision=4
-------------------------------------
****x_width=10,x_fill=&,x_precision=4****
&&&&&&&123 123.4568
123&&&&&&& 123.4568
x_width=0
x_fill=&
x_precision=4
例 7.4 利用控制输入输出格式的成员函数建立对齐的数字表
#include<iostream..h>
#include<math.h>
void main()
{ double x;
cout,precision(4);
cout<<” x sqrt(x) x^2\n\n”;
for(x=1.0;x<=20.0;x++)
{ cout.width(8);
cout<<x<<’ ‘;
cout.width(8);
cout<<sqrt(x)<<’ ‘;
cout.width(8);
cout<<x*x<<’\n’;
}
}
这程序建立了如下的表
x sqrt(x) x^2
1 1 1
2 1.4142 4
3 1.7321 9
4 2 16
5 2.2361 25
6 2.4495 36
7 2.6458 49
8 2.8284 64
9 3 81
10 3.1623 100
11 3.3166 121
12 3.4641 144
13 3.6056 169
14 3.7417 196
15 3.873 225
16 4 256
17 4.1231 289
18 4.2426 324
19 4.3598 361
20 4.4721 400
15
7.3.2使用操作符进行输入输出格式控制
C++提供了另一种进行 I/O格式控制的方法,这一方法使用了一种
称为操纵符 (也称为操纵函数 )的特殊函数。在很多情况下,使用
操纵符进行格式化控制比用 ios格式标志和成员函数要方便。
1,C++预定义的操纵符
C++预定义的操纵符是以一个流引用作为其参数,并返回同一流
的引用,因此它可以嵌入到输入输出操作的链中。操纵符可用来
改变域宽,使整型数以八进制或十六进制形式输入或输出,并设
置填充字符等。许多操纵符的功能类似于上面介绍的 ios类成员
函数的功能。 C++提供的预定义操纵符如下,
( 1 ) dec 以十进制形式输入或输出整型数,可用于输入或输出 。
( 2 ) hex 以十六进制形式输入或输出整型数,可用于输入或输出。
( 3 ) oct 以八进制形式输入或输出整型数,可用于输入或输出。
( 4 ) ws 用于在输入时跳过开头的空白符,仅用于输入。
( 5 ) endl 插入一个换行符并刷新输出流,仅用于输出。
( 6 ) ends 插入一个空字符,通常用来结束一个字符串,仅用于输出。
( 7 ) flush 刷新一个输出流,仅用于输出。
( 8 ) setbase( int n )把转换基数设置为 n(n的值为 0,8,10,或 16),n的缺
省值为” 0”,即以十进制形式输出。
16
( 9 ) resetiosflags (long f ) 关闭由参数 f 指定的格式标志,可用于
输入或输出。
( 10 ) setiosflags (long f ) 设置由参数 f 指定的格式标志,可用于
输入或输出。
( 11 ) setfill (int c ) c 为填充字符,缺省时为空格,可用于输入
或输出。
( 12 ) setprecision (int n )设置数据小数部分的位数,缺省时小数的
位数为 6,可用输入输出。
( 13 ) setw (int n) 设置域宽为 n,可用于输入或输出。
17
操纵符 setiosflags( )和 resetiosflags ( )中所用的格式标志
格式标志名 含 义
ios,,left 输出数据按域宽左对齐输出
ios,,right 输出数据按域宽右对齐输出
ios,:scientific 使用科学计数法表示浮点数
ios,:fixed 使用定点形式表示浮点数
ios,:dec 转换基数为十进制形式
ios,:hex 转换基数为十六进制形式
ios,:oct 转换基数为八进制形式
ios,:uppercase 十六进制形式和科学计数法输出时,表示数值的字
符 一律为大写
ios,:showbase 输出带有一个表示制式的字符 (如” X”表示十六
进
制,”O”表示八进制 )
ios,:showpos 在正数前添加一个” +”号
ios,:showpoint 浮点输出时必须带有一个小数点
2.操纵符的使用
操纵符分为带参数的操纵符和不带参数的操纵符,通常,不带参
数的操纵符在 iostream.h 文件中定义,带参数的操纵符在
iomanip.h 文件中定义。
18
例 7,5 使用操纵符的输入输出格式控制
#include <iostream.h>
#include <iomanip.h>
void main ( )
{ cout <<setw (10)<<123<<567<<endl; //①
cout<<123<<setiosflags(ios::scientific)<<setw(20)<<123.45
6789<<endl; //②
cout <<123<<setw(10)<<hex<<123<<endl; //③
cout <<123<<setw(10)<<oct(8)<<123<<endl; //④
cout <<123<<setw(10)<<setbase(0)<<123<<endl; //⑤
cout<<resetiosflags(ios::scientific)<<setprecision(4)<<123.
456789<<endl;⑥
cout<<setiosflags(ios::left)<<setfill(?#?)<<setw(8)<<123<<en
dl; // ⑦
cout<<resetiosflags(ios::left)<<setfill(?$?)<<setw(8)<<456<<
endl; // ⑧
}
19
程序运行结果为,
1234567 ①
123 1.234567e+02 ②
123 7b ③
7b 173 ④
173 123 ⑤
123.4568 ⑥
123##### ⑦
$$$$$456 ⑧
例 7.6 二次方表与二次方根表的程序的程序的另一个版本
#include<iostream.h>
#include<iomanip.h>
#include<math.h>
void main ( )
{ double x;
cout<<setprecision (4);
cout <<, x sqrt (x) x^2\n\n”;
for (x=1.;x<=20.0;x++);
{
cout <<setw(8)<<x<<? ?;
cout <<setw(8)<<sqrt(x)<<? ?;
cout <<setw(8)<<x*x<<?n?;
}
}
20
3,用户自定义的操纵符
C++除了提供此同预定义的操纵符外,也允许用户自定义操纵符,
把程序中频繁使用的输入输出操作合并成一个操纵符。
若为输出流定义操纵符函数,则定义形式如下,
ostream &manip_name(ostream &stream)
{
//自定义代码
return stream;
}
若为输入流定义操纵符函数,则定义形式如下,
istream &manip_name(istream &stream)
{
//自定义代码
return stream;
}
21
例 7.7 自定义的操纵符
#include <iostream.h>
#include<iomanip.h>
ostream &output 1(ostream &stream)
{ stream.setf(ios::left);
stream<<setw(10)<<hex<<setfill(?&?);
return stream;
}
void main( )
{ cout<<123<<endl;
cout<<output 1<<123<<endl;
}
程序运行结果如下,
123
7b&&&&&&&&
22
例 7.8
#include<iostream.h>
#include<iomanip.h>
istream &inputl (istream &in)
{ cin>>hex;
cout<<,Enter number using hex format:”;
return in;
}
void main ( )
{ int i;
cin>>inputl>>i;
cout <<endl;
}
以上程序中定义了一个操纵符函数 input1,该函数要求输入一
个十六进制数。
程序运行后,屏幕上显示,
Enter number using hex format,
提示用户输入一个十六进制数。
23
7.4用户自定义类型的输入输出
用户自定义的类型数据的输入或输出,可以通过重载运算符
,>>”和, <<”来实现,使,>>”和, <<”可以直接用来输入或输
出类的对象。
7.4.1 重载输出运算符, <<”
定义输出运算符” <<”重载函数的一般格式如下,
ostream &operator <<(ostream &stream,class_name obj )
{
//操作代码
return stream;
}
函数中的一个参数是对 ostream 对象的引用,它可以是其它任何合
法的标示符,但必须与 return后面的标示符相同。第二个参数接
受将被输出的对象,其中 class_name是类名,obj为该类的对象
名。
24
例 7.9输出运算符, <<”重载的例子
#include <iostream.h>
class coord{
public,
int x,y;
coord ( ) {x=0; y=0;}
coord ( int I,int j) {x=I; y=j;}
};
ostream &operator <<(ostream &stream,coord ob)
{ stream <<ob.x<<,,”<<ob.y<<endl;
return stream;
}
void main ( )
{ coord a ( 5,6),b (10,34);
cout <<a<<b;
}
程序运行结果如下,
5,6
10,34
25
例 7.10 把重载输出运算符定义为类的友元函数,这样就可以访问
类的私有成员
#include <iostream.h>
class coord{
int x,y;
public,
coord ( ) {x=0; y=0;}
coord ( int i,int j) {x=i; y=j;}
friend ostream &operator<<(ostream &stream,coord ob);
};
ostream &operator<<(ostream &stream,coord ob)
{ stream<<ob.x <<”,”<<ob.y<<endl;
return stream;
}
void main ( )
{ coord a ( 5,6),b (10,34);
cout <<a<<b;
}
26
程序运行结果如下,
5,6
10,34
7.4.2 重载输入运算符, >>”
只是要把 ostream换成 istream,把” <<”用” >>”代替,第二个
参数是一个引用。
定义输入运算符” >>”重载函数的一般格式如下,
istream &operator >>(istream &stream,class_name &obj )
{
//操作代码
return stream;
}
例 7.11重载输入运算符, >>”
#include<iostream.h>
class three_d{
int x,y,z;
public,
three_d(int a,int b,int c) {x=a; y=b; z=c;}
friend ostream &operator<<(ostream &output,three_d ob);
friend istream &operator>>(istream &input,three_d &ob);
};
27
接 1 例 7.11
ostream &operator<<(ostream &output,three_d ob);
{ output<<ob.x<<,,”;
output<<ob.y<<,,”;
output<<ob.z<<endl;
return output ;
}
istream &operator>>(istream &input,three_d &ob);
{ cout<<”Enter x,y,z value:”;
input>>ob.x;
input>>ob.y;
input>>ob.z;
return input;
}
28
接 2 例 7.11
void main ( )
{ three_d obj (10,20,30); //定义类 three_d的对象 obj
cout<<obj; //输出对象 obj的成员值
cin>>obj; //输出对象 obj的各成员值,将原值覆盖
cout<<obj; //输出对象 obj的成员值 (新值 )
}
程序运行结果如下,
10,20,30
Enter x,y,z value,40 50 60
40,50,60
29
7.5文件的输入输出
C++把文件看作字符序列, 即文件是由一个字符数据顺序组成的 。
根据数据的组织形式, 文件可分为文本文件和二进制文件 。 文本
文件又称 ASCII文件, 它的每个字节存放一个 ASCII代码, 代表
一个字符 。 二进制文件则是把内存中的数据, 按其在内存中的存
储形式原样写到磁盘上存放 。 文件是一个字符流或二进制流, 它
把数据看作是一连串的字符, 而不考虑记录的界限, 它对文件的
存取以字符为单位进行 。 我们把这种文件称为流式文件 。 在 C++中,
要进行文件的输入输出, 必须首先 创建一个流, 然后将这个流与
文件相关联, 即打开文件, 此时才能进行读写操作, 完成后再关
闭这个文件 。 这是 C++中进行文件输入输出的基本过程 。
7.5.1 文件的打开与关闭
1,文件的打开
为了执行文件的输入输出, C++提供了三个流类 。
名 称 基 类 功 能
ofstream 由 ostream 派生 用于文件的输出
ifstream 由 istream 派生 用于文件的输入
fstream 由 iostream 派生 用于文件的输入或输出
30
建立流的过程就是定义流类的对象,例如,
ifstream in;
ofstream out;
fstream both;
它们分别定义了输入流对象 in,输出流对象 out,输入输出流对象
both。
Open( )函数是上述三个流类的成员函数, 其原型为,
void open(const unsigned char *,int mode,
int access=filebuf::openprot);
打开语句,
流 类 的 对 象, open(const unsigned char *, int mode, int
access=filebuf::openprot);
其中第一个参数是用来传递 文件名 的;第二个参数 mode的值决
定文件将 如何 被 打开, 它必须取下面的值中的一个; access的值
决定文件的 访问方式 。
31
ios::app 使输出追加到文件尾部
ios::ate 查找文件尾
ios::in 打开一个文件进行读操作
ios::nocreate 文件不存在时,导致 open( )失败
ios::noreplace 若文件存在,则 open( )失败
ios::out 打开一个文件进行写操作
ios::trunc 使同名文件被删除
ios::binary 文件以二进制方式打开,缺省时为文本文件
访问方式
0 普通文件
1 只读文件
2 隐含文件
4 系统文件
8 备份文件
32
打开文件的步骤,
(1) 定义一个类的对象, 例如,ofstream out;
(2) 使用 open( )函数打开文件 。 例如,out.open(”test”,ios::out, 0);
文件只有在打开后, 才能对文件进行读写操作 。 由于某些原因,
可能打开失败, 因此使用文件之前必须进行执行检测, 以确认打
开一个文件是否成功 。 可以使用类似下面的方法进行检测,
if( ! mystream)
{ cout<<”cannot open file! \n”;
//错误处理代码
}
在实际编程时, 打开一个文件的最常见的形式为,
ofstream out (”test”) ; 用 if (!out) 检测是否打开成功
它相当于语句,
ofstream out;
out.open(”test”);
2,文件的关闭
关闭文件可使用 close( )函数完成, close函数也是流类中的成员函
数, 它不带参数, 没有返回值, 例如,out.close ( );
33
7.5.2 文件的读写
在含有文件操作的程序中, 必须有如下的预处理命令,
# include <fstream.h>
1,文本文件的读写
文件打开后, 文本文件的读写使用运算符, <<”与, >>”,只是必
须用与文件相连接的流代替 cin和 cout。
例 7.12 把一个整数、一个浮点数和一个字符串写到磁盘文件 test
中。
# include <fstream.h>
int main ()
{ ofstream fout(” test”);
if (! fout)
{ cout<<”cannot open output file \n”;
return 1;
}
fout<<10<<”,<<123.456<<” \”This is a test file.\”\n”;
fout.close();
return 0;
}用“写字板”可以看到 test文件的内容,10 123.456,this is a
test file”
34
例 7.13 先建立一个输出文件,向它写入数据,然后关闭文件,再按
输入模式打开它,并读取信息。
# include <fstream.h>
void main ()
{ ofstream fout(” test2”);
if(! fout)
{ cout<<”cannot open output file,\n”; return 1; }
fout<<” Hello! \n”;
fout<<100<<? ?<<hex<<100<<endl;
fout.close ();
ifstream fin(” test2”);
if(! fin)
{ cout<<”cannot open input file,\n”; return 1; }
char str[80];
int I;
fin>>str>>i;
cout<<str<<”,<<i<<endl;
fin.close();
}
程序建立一个输出文件 test2,并向它写入数据,关闭输出文件 test2
。再将文件 test2按输入模式打开,并将字符串,hello! \n”读给字符
数组 str,将整数 100读给整型变量 i。最后在屏幕上显示出 str和 I的值
,如下所示,
Hello! 100
35
例 7.14 从键盘读入字符串,并将它们写进磁盘 文件 。当用户输入
空白字符时,程序停止。
# include <fstream.h>
# include <stdio.h>
void main( int argc,char *argv[])
{ if( argc!=2)
{ cout<<”Usage,write <filename>\n”; return 1; }
ofstream outf (argv[1]);
if (! outf)
{ cout<<”cannot open output file,\n”; return 1; }
char str[60];
cout<<”Write strings to disk,RETURN to stop\n”;
do
{ cout<<”:”;
gets(str);
outf<<str<<endl;
}while (*str);
outf.close ();
}
这个例子采用了命令行参数的形式,输入以下命令行,
<可执行文件名 > <磁盘 文件名 > ↙
程序运行情况如下,
Write strings to disk,RETURN to stop
,abcdefg (从键盘输入一个字符串)
,↙ (输入空行,程序停止)
程序运行后,在磁盘上生成一个名为 <磁盘 文件名 >的文件,
其内容,abcdefg
36
2,二进制文件的读写
任何文件, 无论它包含格式化的文本还是包含原始数据, 都能以
文本方式或二进制方式打开 。 文本文件是字符流, 而二进制文件
是字节流 。
(1) 用 get( )函数和 put( )函数读写二进制文件
get( )是输入流类 istream中定义的成员函数, 它可以从与流对象连
接的文件中读出数据, 每次读出一个字节 (字符 )。 put( )是输出流
类 ostream中的成员函数, 它可以向与流对象连接的文件中写入
数据, 每次写入一个字节 ( 字符 ) 。
get( )函数有许多格式, 其最一般的使用版本的原型如下,
istream &get(unsigned char &ch);
get( )函数从相关流中只读一个字符, 并把该值放如引用 ch&中 。
put( )函数的原型如下,
ostream &put(char ch);
put( )函数将字符 ch写入流中 。
37
例 7.15 实现任意类型的文件拷贝。
# include <iostream.h>
# include <fstream.h>
void main (int argc,char *argv[ ])
{ char ch;
if (argc!=3)
{ cout<<”Bad commond !\n”; return 0; }
ifstream inf (argv[1]);
if (! inf)
{ cout<<”Cannot open souce file.”; return 1; }
ofstream outf (argv[2]);
if (! outf)
{ cout<<”Cannot open target file.”; return 1; }
while (inf)
{ inf.get (ch);
outf.put (ch);
}
inf.close ( );
outf.close ( );
}
假定本程序的文件名为 mvc7_15.cpp,经编译、连接
后生成的可执行文件名为 mvc7_15.exe,则在 DOS下输入
以下命令行,
D> mvc7_15 file1.exe file2.exe
该程序的执行结果,是把文件 file1.exe中的信息拷贝
到文件 file2.exe中
38
eof( )函数的使用
二进制文件的处理过程与文本文件的处理过程基本相同, 但在判
断文件是否结束时有所区别 。 在文本文件中, 遇到文件结束符,
get函数返回一个文件结束标志 EOF,该标志的值为 -1。 但在处
理二进制文件时, 读入某一字节中的二进制数的值可能是 -1,这
与 EOF的值相同 。 这样就有可能出现读入的有用数据被处理成
,文件结束, 的情况 。 为了解决这个问题, c++提供了一个成员
函数 eof,用来判断文件是否真的结束, 其原型为,
int eof( ) ;
当到达文件末尾时, 它返回一个非零值, 否则返回零 。 当输入文
件是键盘时, 其结束符是 ctrl_z,eof( ) 函数返回的值为真 。
39
例 7.16在屏幕上显示任何给定文件的内容 。
# include <iostream.h>
# include <fstream.h>
main (int argc,char *argv[ ])
{ char ch;
if (argc!=2)
{ cout<<”Usage,PR<filename>\n”; return 1; }
ifstream in (argv[1]);
if (! in)
{ cout<<”Cannot open file”; return 1; }
while (! in.eof ( ))
{ in.get (ch);
cout<<ch;
}
return 0;
}
假定本程序的文件名为 mvc7_16.cpp,经编译
、连接后生成的可执行文件名为 mvc7_16.exe,则
在 DOS下输入以下命令行,
D> mvc7_16 file1
该程序的执行结果,是把文件 file1 中的信息在
屏幕上显示 。
40
(2) 用 read( )函数和 write( )函数读写二进制文件
有时需要读写一组数据 (如一个结构变量的值 ),为此 C++提供了
两个函数 read( )和 write( ),用来读写一个数据块 。
① read( )函数,
函数原型,istream &read (unsigned char *buf,int num);
read( )是类 istream中的成员函数, 其功能为:从相应的流中读
取 num个字节 (字符 ),并把它们放入指针 buf所指的缓冲区中 。
调用格式,read(缓冲区首址, 读入的字节数 );
注意:, 缓冲区首址, 的数据类型为 unsigned char*,当输入其它
类型数据时, 必须进行类型转换, 例如,int array[ ]={50,60,
70};定义了一个整型数组 array,
read((unsigned char *)&array,sizeof(array);
对整型数组 array,为了把数据从文件读入到它中, 必须在 read( )
函数中把它转换为 unsigned char *类型 。 由 sizeof( )函数确定
要读入的字节数 。
41
② write( )函数,
函数原型,ostream &write(const unsigned char *buf,int
num);
write( )是流类 ostream的成员函数,利用该函数,可以把 buf所
指的缓冲区 num个字节写到相应的流上。参数的含义及调用注意事
项与 read()函数类似。
注意:如果在 num个字节被读出之前就达到了文件尾,则 read( )只
是停止执行,此时缓冲区包含这些字符。我们可以用另一个成员函
数 gcount( )统计出有多少字符被读出。 gcount( )的原型如下,
int gcount( );它返回所读取的字节数 。
42
例 7.17 用 write( )函数向文件 test中写入双精度数与字符串。
# include <iostream.h>
# include <fstream.h>
# include <string.h>
main( )
{ ofstream outf(”test”);
if (! out)
{ cout<<”cannot open output file,\n”; return 1; }
double num=100.45;
char str [ ]=”This is a test”;
out.write ((char * )&num,sizeof (double ));
out.write (str,strlen (str));
out.close ( );
return 0;
} 程序执行后,屏幕上不显示任何信息,但程序已将双精度
100.45和字符串,this is a test” 以二进制形式写入文件 test中。
用下面的程序 可以读取文件 test中的数据,并在屏幕上显示出来,
以验证前面的程序操作。
43
例 7.18 用 read( ) 函数读取例 7.17中程序所建立的文件。
# include <iostream.h>
# include <fstream.h>
# include <string.h>
main( )
{ ifstream in(, test”) ;
if (! in) { cout<<”cannot open input file,\n”; return 1; }
double num;
char str [80];
in.read ((char * )&num,sizeof (double));
in.read (str,14);
cout<<num<<? ?<<str;
in.close ( );
return 0;
}
程序运行结果为,
100.45 This is a test
44
3,文件的随机读写
前面介绍的文件操作都是按一定顺序进行读写的, 因此称为顺序文
件 。
C++在类 istream及类 ostream中定义了几个与在输出流中随机移动
文件指针的成员函数, 则可以在流内随机移动文件指针, 从而可
以对文件的任意数据进行随机读写 。 用函数 seekg( )和 seekp( )进
行随机访问 。 函数 seekg( )用于输入文件, 函数 seekp( )用于输出
文件 。
45
它们的 函数原型 如下,
istream &seekg (streamoff offset,seek_dir origin);
ostream &seekp (streamoff offset,seek_dir origin);
其中,参数 origin表示文件指针的 起始位置, offset表示相对于这个
起始位置的 位移量 。 seek_dir是一个系统定义的枚举名,origin是枚
举变量。 origin的取值有以下三种情况,
? ios::beg 从文件头开始,把文件指针移动由 offset指定的距离。
offset的值为正数
? ios::cur 从当前位置开始,把文件指针向前 (后 )移动由 offset指定
的距离。 offset 的值为正或负数,正数时向前移,负数时向后移。
? ios::end 从文件尾开始,把文件指针移动由 offset指定的距离,
offset的值为负数进行文件的随机读写时,可以用以下函数 测试 文
件指针的 当前位置,
streampos tellg( );用于输入文件。 streampos tellp( );用于输出
文件。
streampos是在 iostream.h中定义的类型,实际上是 long型的。
46
例 7.19 演示 seekp( )函数。它修改文件中的指定字符。执行时在命
令行指定文件名,其后跟想修改的字符的位置,然后再跟新的字符。
# include <iostream.h>
# include <fstream.h>
# include <stdlib.h>
int main (int argc,char *argv[ ])
{ if (argc!=4)
{ cout<<”Usage,CHANGE<filename> <byte> <char>\n”;
return 1; }
fstream out (argv[1],ios::in|ios::out);
if (! out)
{ cout<<”Cannot open file”<<argv[1]<<”\n”; return 1; }
out.seekp (atoi (argv[2]),ios::beg);
out.put ( *argv[3]);
out.close ( );
return 0;
}
假定上述程序的文件名为 mvc7_19.cpp,经编译、连接生成
的可执行文件名为 mvc7_19.exe。需要修改的文件名为 file
,file的内容为,AAAAAAAAAA1234567890
执行下述命令,
D> mvc7_19 file 5 #
程序运行结果为,
AAAAA#AAAA1234567890
47
例 7.20 使用 seekg( )函数,它从指定的位置开始显示文件内容。
# include <iostream.h>
#include <fstream.h>
# include <stdlib.h>
void main (int argc,char * argv[ ])
{ char ch;
if (argc!=3 )
{ cout<<”Usage,LOCATE<filename><loc>\n”; return 1; }
ifstream inf (argv[1] );
if (! inf)
{ cout<<”Cannot open input file,\n”; return 1; }
inf.seekg (atoi (argv[2]),ios::beg);
while (! inf.eof ( ))
{ inf.get (ch);
cout<<ch;
}
inf.close ( );
}
假定程序的执行文件文件名为 mvc7_20.exe,要显示文件
名为 file,希望从第 5个位置开始显示, 则执行的命令行,
D>mvc7_20 file 5 <cr>
程序运行结果为,
#AAAA1234567890
42
48
7.6 应 用 举 例
例 7.21 重载运算符, <<”和, >>”,使用户能直接输入和输出复数 。
# include<iostream.h>
class complex{
double real,imag;
public,
complex(double r,double i) { real=r; imag=i;}
complex( ) { real=0; imag=0;}
friend complex operator+(complex,complex);
friend ostream &operator<<(ostream&,complex&);
friend istream &operator>>(istream&,complex&);
};
complex operator+(complex a,complex b)//定义重载运算符, +”
{ complex temp;
temp.real=a.real+b.real;
temp.imag=a.imag+b.imag;
return temp;
}
49
接 1 例 7.21
ostream &operator<<(ostream &output,complex &obj) //定
义重载运算符,<<”
{ output<<obj,real;
if(obj.imag>0) output<<”+”;
if(obj.imag!=0) output<<obj.imag<<“i”;
return output;
}
istream &operator>>(istream &input,complex &obj) //定义
重载运算符,>>”
{ cout<<”Input the real,imag of the complex:\n”;
input>>obj.real;
input>>obj.imag;
return input;
}
50
void main()
{ complex c1(2.4,4.6),c2,c3;
cout<<”the value of c1 is:”<<c1<<endl;
cin>>c2;
cout<<”the value of c2 is:”<<c2<<endl;
c3=c1+c2;
cout<<”the value of c3 is:”<<c3<<endl;
}
程序运行后屏幕显示为,
the value of c1 is,2.4+4.6i
Input the real,imag of the complelx,
若此时输入,
3.7 2.5
屏幕上又显示出,
the value of c2 is 3.7+2.5i
the value of c3 is 6.1+7.1i
51
例 7.22 下面的程序建立了类 triangle,用来存储直角三角形的宽与
高。这个类的重载输出运算符函数在屏幕上显示三角形。
# include<iostream.h>
class triangle{
int height,base;
public,
triangle (int h,int b) { height=h; base=b; }
friend ostream &operator << (ostream &stream,triangle
ob);
};
ostream &operator<<(ostream &stream,triangle ob)
{ int I,,j,h,k;
i=j=ob.base-1;
for (h=ob.height-1;h;h--)
{ for (k=i;k;k--)
stream<<? ?;
stream<<?*?;
52
接 1 例 7.22
if (j!=i)
{ for(k=j-i-1;k;k--)
stream<<? ?;
stream<<?*?;
}
i--;
stream<<endl;
}
for (k=0;k<ob.base;k++) stream<<?*?;
stream<<endl;
return stream;
}
void main ()
{ triangle t1(5,5),t2(10,10),t3(12,12);
cout<<t1<<endl;
cout<<t2<<endl;
cout<<t3<<endl;
}
53