第 9章 流类库与输入 /输出第 9章 流类库与输入 /输出
9.1 输入 /输出标准流类
9.2 文件流类
9.3 串流类
9.4 控制符第 9章 流类库与输入 /输出
9.1 输入 /输出标准流类
9.1.1 输入 /输出流的概念就像 C语言一样,C++语言中也没有输入 /输出语句 。
C++的 I/O是以字节流的形式实现的,每一个 C++编译系统都带有一个面向对象的输入 /输出软件包,这就是
I/O流类库 。 其中,流是 I/O流类的中心概念 。 到目前为止,我们一直在使用它 。
第 9章 流类库与输入 /输出所谓流,是指数据从一个对象流向另一个对象 。
在 C++程序中,数据可以从键盘流入到程序中,也可以从程序中流向屏幕或磁盘文件,把数据的流动抽象为,流,。 流在使用前要被建立,使用后要被删除,
还要使用一些特定的操作从流中获取数据或向流中添加数据 。 从流中获取数据的操作称为提取操作,向流中添加数据的操作称为插入操作 。
第 9章 流类库与输入 /输出流实际上就是一个字节序列 。 在输入操作中,字节从输入设备 (如键盘,磁盘,网络连接等 )流向内存;
在输出操作中,字节从内存流向输出设备 (如显示器,
打印机,磁盘,网络连接等 )。 如图 9-1所示 。
在 C++语言中,针对流的特点,提供了如图 9-2所示的层次结构来描述流的行为,并给出了 I/O流类库的操作 。
第 9章 流类库与输入 /输出第 9章 流类库与输入 /输出图 9-2 输入 /输出流类层次图第 9章 流类库与输入 /输出表 9-1 I/O流类列表第 9章 流类库与输入 /输出
9.1.2 输入 /输出标准流类
1,标准流的设备名由表 9-1可见,I/O流的标准头文件是 iostream.h。 其中,ostream类通过其派生类 ostream_withassign支持以下预先定义的流对象:
cout:标准输出 。 默认设备为屏幕 。
cerr:标准错误输出 。 没有缓冲,发送给它的内容立即被输出,默认设备为屏幕 。
第 9章 流类库与输入 /输出
clog:标准错误输出 。 有缓冲,当缓冲区满时被输出,默认设备为打印机 。
而 istream类通过其派生类 istream_withassign支持预先定义的对象 。
cin:标准输入。默认设备为键盘。
第 9章 流类库与输入 /输出
2,原理
cout 是 ostream 类 的 全 局 对 象,它 在 头 文 件
iostream.h中的定义如下:
ostream cout(stdout); //这里,stdout作为该对象构造时的参数对应每种基本数据类型,ostream类都存在友元,它们都在 iostream.h中声明 。 例如:
ostream& operator<<(int n);
ostream& operator<<(float f);
ostream& operator<<(const char*psz);
//...
第 9章 流类库与输入 /输出如语句:
cout<<"How old are you? ";
cout是类 ostream的对象,<<是插入运算符,右面是 char*类型,所以,应该匹配上面第三个操作符 。 它将整个字符串输出,并返回 ostream流对象的引用 。
同理,cin是 istream的全局对象,istream类也存在友元 。 例如:
istream& operator>>( int &n);
istream& operator>>( float &f);
istream& operator>>( char*psz);
//...
第 9章 流类库与输入 /输出
9.2 文件流类
fstream,ifstream和 ofstream是文件流类,在头文件
fstream.h中定义。其中,fstream是 ofstream和 ifstream多重继承的子类。文件流类不是标准设备,没有 cout那样预先定义的全局对象。文件流类支持对磁盘文件的操作。要定义一个文件流类对象,须指定文件名和打开方式。
第 9章 流类库与输入 /输出类 ofstream用于执行文件输出,该类有以下几个构造函数:
ofstream::ofstream( filedesc fd );
ofstream::ofstream( filedesc fd,char*pch,int nLength );
ofstream::ofstream( const char*szName,
int nMode = ios::out,
int nProt = filebuf::openprot );
第 9章 流类库与输入 /输出类 ifstream用于执行文件输入,该类有以下几个构造函数:
ifstream::ifstream( filedesc fd );
ifstream::ifstream( filedesc fd,char*pch,int nLength );
ifstream::ifstream( const char*szName,
int nMode = ios::in,
int nProt = filebuf::openprot );
第 9章 流类库与输入 /输出其中最常用的都是最后一个构造函数 。 该函数有三个参数,第一个参数是指向要打开的文件名的字符串,后两个参数指定文件的打开模式 。 文件打开模式的具体标志见表 9-2。 可以用按位 OR( |) 运算符组合这些标志,它们作为枚举器定义在 ios类中 。
第 9章 流类库与输入 /输出表 9-2 文件打开模式第 9章 流类库与输入 /输出打开一个输出文件,用于在文件尾添加数据打开一个现存文件 (用于输入或输出 )并查找到结尾打开一个输入文件 。 对于一个 ofstream文件,使用
ios::in作为一个 openmode,可避免删除一个现存文件中现有的内容打开一个文件,用于输出 。 对于所有 ofstream对象,
此模式是隐含指定的如果一个文件存在,则打开它;否则该操作失败第 9章 流类库与输入 /输出如果一个文件不存在,则作为新文件打开它;如果文件已存在,则该操作失败打开一个文件 。 如果它已经存在,则删除其中原有的内容 。 如果指定了 ios::out,但没有指定 ios::ate、
ios::app和 ios::in,则隐含为此模式以二进制模式打开一个文件 (默认是文本模式 )
Nprot是文件保护方式,它的标志如表 9-3。
第 9章 流类库与输入 /输出表 9-3 文件保护方式第 9章 流类库与输入 /输出
【 例 9-1】 向文件 myfile中写入一些信息 。
#include<fstream.h>
void main( )
{
ofstream fc("c:\\temp\\myfile");
fc<<"Constructs an ofstream object.\n"
<<"All ofstream constructors construct a filebuf object.
\n";
}
第 9章 流类库与输入 /输出注意:
① 这里的文件名要说明其路径,要使用双斜杠,
因为 C++编译器理解单斜杠为字符转换符 。
② 在文件打开时,匹配了构造函数
ofstream::ofstream(char*),只需要一个文件名,其它为默认。打开方式默认为 ios::out | ios::trunc,即该文件用于接受程序的输出。如果该文件已存在,则其内容必须先清除,否则就新建。
第 9章 流类库与输入 /输出如果要检查文件是否打开,则须判断成员函数 fail( ):
#include <fstream.h>
void func( )
{
ofstream fc("myfile");
if(fc.fail( )) //fail( )= =1
{
cerr<<"error opening file\n";
return;
}
fc<<"...";
第 9章 流类库与输入 /输出
}
若要打开一个输入文件,则有
//...
ifstream fc("myfile",ios::nocreate);
//...
当然,也可以通过检查 fc.fail( )来确定文件打开是否出错 。
如果要打开一个同时用于输入和输出的文件,则有
//...
fstream fc("myfile",ios::in | ios::out);
//...
第 9章 流类库与输入 /输出
9.3 串流类
strstream,istrstream和 ostrstream是串流类,在头文件 strstrea.h中定义 。 其中,strstream是 istrstream和
ostrstream多重继承的子类 。 同样,串流类也不是标准设备,它没有 cout那样预先定义的全局对象 。 串流类允许将 fstream类定义的文件操作应用于存储区中的字符串,即将字符串看作为设备 。 要定义一个串流类对象,
须提供字符数组和数组大小 。
第 9章 流类库与输入 /输出类 ostrstream用于执行串流输出,该类有以下几个构造函数:
ostrstream( );
ostrstream( char*pch,int nLength,int nMode =
ios::out );
其中比较常用的是第二个构造函数,它有三个参数 。 第一个参数指出字符数组,第二个参数说明数组的大小,第三个参数指出打开方式 。
第 9章 流类库与输入 /输出类 istrstream用于执行串流输入,该类有以下几个构造函数:
istrstream( char*pch );
istrstream( char*pch,int nLength );
这两个构造函数都比较常用 。 Char*pch参数指出了字符数组,int nLength参数说明数组的大小 。 当
nLength为 0时,表示把 istrstream类对象连接到由 pch指向的以空字符结束的字符串 。
第 9章 流类库与输入 /输出例如,下面的程序代码定义一个串流类对象,并对其进行输入操作:
char str[50]="How are you!\n";
char a;
istrstream ss(str);
ss>>a;
cout<<a<<endl;
输出结果为
H
第 9章 流类库与输入 /输出
【 例 9-2】 使用串流输入对字符串中的数据进行解读 。
#include<iostream.h>
#include<strstrea.h>
char*ioString(char*);
void main( )
{
char*str="100 123.456";
char*Buf0=ioString(str);
cout<<Buf0<<endl;
}
第 9章 流类库与输入 /输出
char*ioString(char*pString)
{
istrstream inS(pString,0); //以 ios::in方式
int iNumber;
float fNumber;
inS>>iNumber>>fNumber; //从串流中读入一个整数和浮点数
char*Buf1=new char[28];
ostrstream outS(Buf1,28);
outS<<"iNumber="<<iNumber
<<",fNumber="<<fNumber<<endl;
return Buf1;
第 9章 流类库与输入 /输出
}
程序运行结果为
iNumber=100,fNumber=123.456
分析:在函数 ioString( )中,以 pString为输入设备,
先定义一个输入串流对象 inS,从中输入一个整数和一个浮点数 。 再开辟一个字符串空间作为输出设备,定义一个输出串流对象 outS,将从输入设备输入的两个变量的值输出 。
第 9章 流类库与输入 /输出
9.4 控制符
9.4.1 使用流对象的成员函数
【 例 9-3】 使用 width成员函数控制输出宽度 。
#include <iostream.h>
void main( )
{
double values[ ]={1.44,36.47,625.7,4096.24};
for(int i=0;i<4;i++)
{
第 9章 流类库与输入 /输出
cout.width(10);
cout<<values[i]<<'\n';
}
}
程序运行结果为
1.44
36.47
625.7
4096.24
第 9章 流类库与输入 /输出此例子在一列中以至少 10个字符宽按右对齐方式输出数据 。 从程序的输出结果可以看到,在少于 10个字符宽的数值前加入了引导空格 。
空格是默认的填充符,当输出的数据不能充满指定的宽度时,系统会自动以空格填充 。 另外,还可以使用 fill成员函数为已经指定宽度的域设置填充字符的值 。 为了用星号填充数值列,我们可以将例 9-3中的 for
循环修改如下:
第 9章 流类库与输入 /输出
for(int i=0;i<4;i++)
{
cout.width(10);
cout.fill('*');
cout<<values[i]<<endl;
}
其输出结果为
******1.44
*****36.47
*****625.7
***4096.24
第 9章 流类库与输入 /输出
9.4.2 使用控制符
C++的输入 /输出流类库提供了一些控制符,可以直接嵌入到输入 /输出语句中来实现对 I/O格式的控制 。
它的优点是程序可以直接将控制符插入流中,而不必单独调用 。 表 9-4中列出了常用的 I/O流类库控制符 。
第 9章 流类库与输入 /输出表 9-4 常用的 I/O流类库控制符第 9章 流类库与输入 /输出第 9章 流类库与输入 /输出
【 例 9-4】 使用 setw控制符指定宽度 。
#include<iostream.h>
#include<iomanip.h>
void main( )
{
double values[ ]={1.44,36.47,625.7,4096.24};
char*names[ ]={"Rose","John","Alice","Mary"};
for(int i=0;i<4;i++)
第 9章 流类库与输入 /输出
cout<<setw(6)<<names[i]<<setw(10)<<values[i]<<endl;
}
width成员函数在头文件 iostream.h中说明 。 如果带参量使用 setw(n)或任何其它控制符,还必须包括头文件 iomanip.h。 在输出中,字符串输出在宽度为 6的域中,整数输出在宽度为 10的域中 。 程序运行结果为
Rose 1.44
John 36.47
Alice 625.7
Mary 4096.24
第 9章 流类库与输入 /输出
setw和 width都不截断数值 。 如果一个数值需要比
set(n)确定的字符数更多的字符,则该值将使用它所需要的所有字符 。 当然,还要遵守该流的精度设置 。
setw和 width仅影响紧随其后的域,即使用 setw和 width
设置的间隔方式并不保留其效力 。 在一个域输出完后,
域宽度恢复成它的默认值 (必要的宽度 ),但其它流格式选项保持有效直到发生改变 。
第 9章 流类库与输入 /输出例如,下面的程序代码:
//...
cout <<setiosflags(ios::right) //设置为默认的右对齐方式,
<<setw(5)<<1
<<setw(5)<<2
<<setw(5)<<3<<endl;
cout <<setiosflags(ios::left) //设置成左对齐方式第 9章 流类库与输入 /输出
<<setw(5)<<1
<<setw(5)<<2
<<setw(5)<<3<<endl;
cout<<resetiosflags(ios::left) //关闭左对齐标志
//...
这段程序代码中,是通过使用带参数的 setiosflags
控制符来设置左、右对齐,参数是 ios::left和 ios::right枚举器。
第 9章 流类库与输入 /输出该枚举器定义在 ios类中,因此,引用时必须包括
ios::前缀 。 这里需要用 resctiosflags操纵符关闭左,右对齐标志 。 setiosflags不同于 width和 setw,它的影响是持久的,直到用 resetiosflags重新恢复默认值时为止 。
这段程序代码的输出结果为
1 2 3
1 2 3
常用控制符和流格式控制成员函数如表 9-5所示。
第 9章 流类库与输入 /输出表 9-5 常用控制符和流成员函数第 9章 流类库与输入 /输出
9.5 输入 /输出成员函数
9.5.1 使用成员函数输入
1,getline( )函数在程序使用 cin输入时,cin用空白符和行结束符将各个值分开。有时候输入可能需要读取一整行文本并且分开不同的域,为此,我们可以使用 getline成员函数。
其函数原型如下:
istream&getline( char*pch,int nCount,char delim = '\n' );
第 9章 流类库与输入 /输出其中,第一个参数是字符数组,用于放置读取的文本;第二个参数是本次读取的最大字符个数;第三个参数是分隔字符,作为读取一行结束的标志 。
getline成员函数的功能是允许从输入流中读取多个字符 (包括空白字符和行结束符 ),并且允许指定输入终止字符 (默认值是换行字符 )。在读取完成后,从读取的内容中删除该终止字符。
第 9章 流类库与输入 /输出
【 例 9-5】 为输入流指定一个终止字符 。
本程序连续读入一串字符,直到遇到字符 t时停止,
字符个数最多不超过 99个 。 程序中的 t是大小写敏感的 。
#include<iostream.h>
void main( )
{
char line[100];
cout<<"Type a line terminated by 't'"<<endl;
cin.getline(line,100,'t');
cout<<line<<endl;
}
第 9章 流类库与输入 /输出
2,get( )函数在输入时,有些时候需要执行每次只输入单个字符的操作,我们可以使用 get( )成员函数来完成 。 get( )函数的格式如下:
char istream::get( );
【 例 9-6】 循环读入字符,直到键入一个 y字符,或遇到文件尾 。
#include<iostream.h>
void main( )
{
char letter;
while(!cin.eof( ))
第 9章 流类库与输入 /输出
{
letter=cin.get( );
if(letter=='y')
{
cout<<"'y'be met!";
break;
}
cout<<letter;
}
}
第 9章 流类库与输入 /输出
get( )函数还有一种形式可以输入一系列字符,直到输入流中出现结束符或所读字符个数已达到要求读的字符个数 。 这时,get( )函数的函数原型如下:
istream&istream::get(char*,int n,char delim='\n');
第 9章 流类库与输入 /输出例如,下面程序输入一系列字符,将前 24个字符输出 。
#include <iostream.h>
void main( )
{
char line[25];
cout << " Type a line terminated by carriage return\n>";
cin.get( line,25 );
cout << ' ' << line;
}
第 9章 流类库与输入 /输出
9.5.2 使用成员函数输出
【 例 9-7】 使用 put( )成员函数,在屏幕上显示字母表中的字母 。
#include<iostream.h>
void main( )
{
char letter;
for(letter='A';letter<='Z';letter++)
cout.put(letter);
}
第 9章 流类库与输入 /输出程序运行结果为
ABCDEFGHIJKMNOPQRSTUVWXYZ
也可以像下面那样在一条语句中连续调用 put( )函数:
cout.put('A').put('\n');
该语句在输出字符 A后输出一个新换行符 。
还可以用 ASCII码值表达式调用 put( )函数:
cout.put(65);
该语句也输出字符 A。
第 9章 流类库与输入 /输出
9.6 用户自定义类型的输入 /输出
【 例 9-8】 用户自定义的插入运算符和提取运算符 。
#include<iostream.h>
class PhoneNumber
{
private:
char nationCode[4];
char areaCode[4];
char phoneCode[8];
第 9章 流类库与输入 /输出
public:
friend ostream& operator<<(ostream&,PhoneNumber&);
friend istream& operator>>(istream&,PhoneNumber&);
};
ostream& operator<<(ostream& output,PhoneNumber& num)
{
output<<"("<<num.nationCode<<")"
<<num.areaCode<<"-"<<num.phoneCode;
return output;
第 9章 流类库与输入 /输出
}
istream& operator>>(istream& input,PhoneNumber& num)
{
input.ignore( ); //跳过 (
input.getline(num.nationCode,4);
input.ignore( ); //跳过 )
input.getline(num.areaCode,4);
input.ignore( ); //跳过 -
input.getline(num.phoneCode,8);
return input;
第 9章 流类库与输入 /输出
}
void main( )
{
PhoneNumber phone;
cout<<"Enter a telephone number in the"
<<" form(086)029-5261111\n";
cin>>phone;
cout<<"The phone number entered was:\n"
<<phone<<endl;
}
第 9章 流类库与输入 /输出程序运行结果为输入,( 086) 029-1234567
The phone number entered was:
( 086) 029-1234567
该程序为处理用户自定义的电话号码类
PhoneNumber的数据重载了这两个运算符。另外,该程序假定电话号码的输入是正确的。
第 9章 流类库与输入 /输出提取运算符的参数是对 istream对象的引用和对自定义类型对象的引用,返回对 istream对象的引用。在该程序中,重载的提取运算符用于把形如 (086)029-
5261111的电话号码输入到 PhoneNumber类型的对象中。
运算符函数分别将电话号码的三个部分分别读到被引用的 PhoneNumber对象的成员 nationCode,areaCode和
phoneCode中 (在运算符函数中,被引用对象是 num;
在 main函数中,被引用对象是 phone)。
第 9章 流类库与输入 /输出调用成员函数 ignore( )去掉了括号和破折号 。 运算符函数返回 istream&类型的引用 input。 通过返回对流引用,我们可以在一条语句中执行完对类 PhoneNumber
的对象的输入操作后,继续执行对类 PhoneNumber的其它对象 或其它 数据 类型的 输入操 作 。 如 两个
PhoneNumber对象可以按下列方式输入:
cin>>phone1>>phone2;
第 9章 流类库与输入 /输出插入运算符的两个参数是对 ostream对象的引用和对自定义类型 ( 本例中为 PhoneNumber) 的对象的引用,返回对 ostream对象的引用 。 在该程序中,重载的插入运算符按输入格式显示类 PhoneNumber的对象 。
该运算符函数将电话号码各部分显示为字符串,因为它们是以字符串格式存储的 ( 类 istream中的成员函数
getline在结束输入后存储一个空字符 ) 。
第 9章 流类库与输入 /输出重载的运算符函数在类 PhoneNumber中被声明为友元函数 。 为了能够访问类中非公有成员,重载的输入和输出运算符必须被声明为类的友元 。
C++允许为用户自定义类型增加新的输入 /输出能力,而无需修改类 ostream或 istream中的声明和私有数据成员。这大大提高了 C++的可扩展性。