C/C++程序设计
1
C++ 语言本身没有输入输出函数。 C语言中是通过 FILE描述的结构进行 I/O处理,C++的 I/O类库平行于这个 FILE结构建立一套面向对象的输入输出方式。 FILE结构的实例称为流文件,I/O类的实例称为流对象。
流对象提供两个特点:一个是数据类型安全的自动匹配,另一个是可以对集合类型数据进行输入输出重载。
C/C++程序设计
2
六、流对象磁盘文件的输入输出七、无格式转换的读写函数 read 和 write
八、文件的定位
C/C++程序设计
3
六、流对象磁盘文件的输入输出流对象磁盘文件的输出输入操作是通过类
fstream,ofstream和 ifstream中的成员函数进行的。
文件流类库 fstream进行输入输出操作;
ofstream 类进行输出操作;
ifstream 类进行输入操作。
C/C++程序设计
4
1,文件的打开函数文件的打开函数就是为要操作的磁盘文件进行必要初始化的函数,这些初始化函数在面向对象编程中是构造函数和
open函数,单一的 fopen函数的功能被分流到流类库的若干构造函数中。
下面对这些构造函数以及相应的 open 成员函数分别予以介绍。
C/C++程序设计
5
ofstream类提供输出文件的功能,定义一个该类的对象如 fout就可以同 cout写数据一样,将数据格式化后存到指定的磁盘文件。另外尚可进行二进制的写操作。
ofstream类需采用 #include<fstream.h>指令。下面是构造函数和 open函数的函数原型:
ofstream ();
ofstream ( const char *filename,int mode = ios::out,
int access = filebuf::openprot );
void open ( const char * filename,int mode = ios::out,
int access = filebuf::openprot );
ofstream (int fd);
ofstream (int fd,char * pch,int length );
C/C++程序设计
6
最后一个参数 access是可选项,在新的 I/O类版本中无
access参数,缺省设置是文件的保护访问模式 (其它模式参阅相关手册 )。
最后两个构造函数采用了文件指示符,它们与系统运行库函数 _open等的低级句柄相关,详情参阅相关文档。
文本模式输出是缺省设置。无参构造函数定义一个对象但不调用 open函数,第二个构造函数和 open函数具有相同的机制,它们也是最常用的。
C/C++程序设计
7
第一个参数 filename和第二个参数 mode与如下的 fopen
函数有相似的解释。
FILE * fopen(const char *filename,const char *mode);
第一个参数 filename就是定位磁盘文件路径的文件名,
该文件名以只读字符串的形式提供。
在使用含路径的文件名时,注意,\”的使用,对于硬盘中的文件,
c:\mydocu_1\ex.cpp
应写成,
"d:\\mydocu_1\\ex.cpp "
或者进行如下的初始赋值,
const char *filename="c:\\mydocu_1\\ex.cpp";
C/C++程序设计
8
第二个参数 mode可称为文件访问操作的模式参数。
mode的取值于文件访问操作的模式常数。这些常数在 ios类中是 enum open_mode 梅举引入的立即数,其名称和含义如下:
in 以 input 方式打开一个输入文件。
out 以 output方式打开一个输出文件。
ate 打开文件时定位于文件的末尾。
app 以 append方式打开一个文件,在文件末尾追加。
C/C++程序设计
9
trunc 如果文件存在,则删除原先的内容并作为新文件打开。
nocreate 打开一个已存在的文件,如果文件不存在则打开失败。
noreplace 打开一个不存在的文件,如果文件已存在则打开失败。
binary 以二进制方式打开文件。
ios::in|ios::out 以读写的方式打开文件。
C/C++程序设计
10
ifstream类提供读取文件的功能,定义一个该类的对象如 fin就可以同 cin读取数据一样,将格式化后的磁盘文件数据读到内存。尚可进行二进制的读取操作。下面是该类的构造函数和 open函数的原型,入口参数具有与 ofstream类
open函数相同的解释。
ifstream ();
ifstream ( const char * filename,int mode=ios::in,
int access = filebuf::openprot );
void open ( const char * filename,int mode=ios::in,
int access = filebuf::openprot );
C/C++程序设计
11
文本模式输入是缺省设置。为进行二进制读取,可以设置 mode参数等于 ios::int|ios::binary。
无参构造函数定义一个对象但不调用 open函数,因此可以先定义调用无参构造函数的对象,然后调用 open函数。或者定义对象时直接调用有参构造函数,因为有参构造函数和 open函数具有相同的运作机制。
C/C++程序设计
12
fstream类提供读写文件的功能,定义一个该类的对象如 fio就可以将格式化后的磁盘文件数据读写到内存。另外尚可进行二进制的读写操作。下面是该类的构造函数和 open
函数的原型,入口参数具有与 ofstream类 open函数相同的解释。
fstream();
fstream ( const char * filename,int mode,
int access = filebuf::openprot );
void open ( const char * filename,int mode,
int access = filebuf::openprot );
C/C++程序设计
13
2,文件的关闭函数 close
在用前面介绍的函数成功打开一个文件之后,就可以对指定的磁盘文件进行操作。在对磁盘文件进行输入输出后,
应该进行内存释放的及时清理工作。 ofstream,fstream和
Ifstream 类中都提供一个 close成员函数关闭相应 open成员函数打开的资源,这些函数完成缓冲区数据的提交,并解除与相关磁盘文件的关联。 close成员函数的原型统一地是:
void close();
有参构造函数定义的对象可由析构函数执行清理工作。
C/C++程序设计
14
[例 ] 格式转换处理一个结构变量
#include<fstream.h>
typedef struct SData
{ int nLineset; float fAdjust;int nPoint;
float fXa; float fYa; float fZa;
}
CData;
C/C++程序设计
15
void OutputData (const char *filename,const CData&d)
{ ofstream fout;
fout.open ( filename,ios::out );
fout<<d.nLineset<<" "<<
d.fAdjust<<" "<<d.nPoint<<" "; //1
fout<<d.fXa<<" "<<
d.fYa<<" "<<d.fZa<<endl; //2
fout.close();
}
C/C++程序设计
16
void InputData (const char *filename,CData &d )
{ ifstream fin;
fin.open ( filename,ios::in );
fin>>d.nLineset>>d.fAdjust>>d.nPoint; //1
fin>>d.fXa>>d.fYa>>d.fZa; //2
fin.close();
}
C/C++程序设计
17
void main(void)
{ CData s = { 1,2,3,4,5,6 };
OutputData ( "c:\\sdata.out",s );
CData d;
InputData ( "c:\\sdata.out",d );
OutputData ( "c:\\cdata.out",d );
}
在文件 c:\cdata.out中输出结果为,1 2 3 4 5 6
在文件 c:\sdata.out中输出结果为,1 2 3 4 5 6
C/C++程序设计
18
3,测试状态的函数 (bad,eof,good,clear等 )
在 ios类中一组测试输入输出状态的 inline成员函数,它们的原型和含义说明如下:
int bad() const; 如果发生错误返回非 0值,相当于设置 badbit错误状态。
void clear (int s = 0); 如果未指定参数则清除状态标志;否则将标志设置为入口值。
int good() const; 若无错误发生返回非 0值。
int eof() const; 如果到达文件结尾返回非 0值,相当于设置 eofbit标志。
C/C++程序设计
19
int fail() const; 如果操作失败,返回非 0值。相当于设置 badbit或 failbit标志。
int rdstate() const; 返回最后一次操作的状态。
状态值由如下 ios类的梅举引入,clear入口值 s也取值如下:
enum io_state
{ goodbit = 0x00,//无错误
eofbit = 0x01,//到达文件结尾
failbit = 0x02,//一个可能恢复的错误
badbit = 0x04 //一个致命的错误
};
C/C++程序设计
20
4,字符和字符串读写函数
istream 类中读取字符的重载函数,下面是有代表性的几个版本。其原型和含义为:
int get(); 从流中读取字符并返回它
Istream & get ( char & ch ); 读取字符到 ch。
Istream & get ( char *buf,int n,char z = '\n' );
//这个函数与下面的函数几乎没有差异
Istream & getline ( char *buf,int n,char z = '\n' );
C/C++程序设计
21
第一个参数匹配存放字符串的数组名,第 2个参数是读入数组的最多字符个数,最后一个参数 z表示结束读取的判断字符,可以灵活指定判断字符。例如‘ x’。缺省的结束读取的判断字符为换行符。最多读 n-1个字符到 buf数组中,追加
‘ \0’到读入字符的结尾。例如程序块,
{ char buf[23]; cin.getline (buf,5,'x' ); cout<<buf; }
键入字符序列 123x45?之后输出 123。
键入字符序列 12345x?之后输出 1234。
但程序块,
{char buf[23];cin>>buf; cout<<buf;}
键入字符序列 123x45?之后输出 123 x45。
C/C++程序设计
22
[例 ] get.cpp算例 (要求这个源程序以文件名 get.cpp保存 C
根目录上 )
# include <fstream.h>
void main (void)
{ ifstream fin ( "c:\\get.cpp",ios::in );
if (fin.fail()) { cout<<"open failed"; return; }
int I = 0;
char buffer [ 512 ];
while ( !fin.eof() && i<512 )
buffer [ i++ ] = fin.get();
buffer [ i ] = '\0'; cout<<buffer;
} //程序运行的结果显示 get.cpp在屏幕上。
C/C++程序设计
23
[例 ] getline.cpp算例
# include <fstream.h>
void main (void)
{ fstream fin ("c:\\ getline.cpp",ios::in);
if(fin.fail()!=0) { cout<<"open failed"; return;}
int i=0;
char buffer[512];
fin.getline ( buffer,518,‘}’ );
//此处等价于 fin.get ( buffer,518,'}‘ );
cout<<buffer<<endl;
}
C/C++程序设计
24
# include <fstream.h>
void main(void)
{ fstream fin ( "c:\\ getline.cpp",ios::in );
if(fin.fail()!=0) { cout<<"open failed"; return; }
在 ostream类中提供 put成员函数,其中的一个函数原型为:
ostream& put(char ch);
put函数的形参 ch正是要存写到 ostream关联的磁盘文件中去的字符,该函数返回 ostream流对象引用。
C/C++程序设计
25
[例 ] put.cpp
# include <fstream.h>
void main (void)
{ fstream fin ("c:\\put.cpp",ios::in);
if(fin.fail()) { cout<<"open failed"; return; }
fstream fout;
fout.open ("c:\\putx.cpp",ios::out);
if (!fout.fail()) { cout<<"open failed"; return; }
while (!fin.eof() )
fout.put ((char)fin.get());
fout.close(); } // 程序运行之后即将上面的 put.cpp源程序转存到 putx.cpp中
C/C++程序设计
26
说明:
上面 put函数入口参数是 char类型,调用时采用强制类型转换 put((char)fin.get()),是为了剔除函数重载的歧义。
因为 put存在另外两个版本,
1,put(unsigned char)
2,put(signed char)
put (unsigned char)版本是基本的成员函数,另外两个版本是这个版本的内联映射:
inline ostream& ostream::put (char _c)
{ return put ((unsigned char) _c); }
inline ostream& ostream::put (signed char _c)
{ return put ((unsigned char) _c); }
C/C++程序设计
27
七、无格式转换的读写函数 read 和 write
前面介绍的函数可以用于读写一个字符或字符串,格式化函数对变量逐个进行格式转换,而此处的函数可进行二进制数据的读取而无须进行格式转换。
读写过程数据的信息未发生转换,因而空间效率和时间效率都比较高。
C/C++程序设计
28
1,read读取函数
istream类成员函数 read的原型为:
istream& read (char *pDst,int size);
第一个参数 pDst定位读取数据的存放位置,可以匹配任意类型的起始地址,但调用时应进行强制类型转换。
第二个参数 size指出等待读取的字节数,可以设置为,
size = n*sizeof (type)
如果 type取值为类类型名如 CType,则当
size = n*sizeof(CType),表示读取 n个对象。 n等于 1时读取一个对象。 type也可以是 double,此时表示读取的数据是 double型的变量。读入数据的来源在调用该函数的流对象中。
C/C++程序设计
29
2,write存写函数
ostream类成员函数 write的原型为:
ostream& write (const char * pSrc,int size);
第一个形参 pSrc定位所写数据的源位置,可匹配任意类型的起始地址,但调用时应进行强制类型转换。
第二个参数 size指出等待存写的字节数,可以设置为,
size = n*sizeof (type)
如果 type取值为 double,则当
size = n*sizeof (double)
表示读取 n个 double变量。 n等于 1 时存写一个 double变量等等。待存写的目的地为与流对象相关联的磁盘文件。
一般地 read 总是读取原先由 write存写的数据。
C/C++程序设计
30
[例 ] 二进制方式读写磁盘文件
#include<fstream.h>
#include<iomanip.h>
typedef struct SData
{ int n;
float f ;
int m;
float x;
float y;
float z;
void PrintData()const;
} CData;
C/C++程序设计
31
inline void WriteData(CData pSrc[],int n,fstream& fpDst)
{ fpDst.write((const char *)pSrc,n*sizeof(CData));
//将数组 pSrc[n]转存到 fpDst的文件
}
inline void ReadData(CData* pDst,int n,fstream& fpSrc)
{ fpSrc.read((char *)pDst,n*sizeof(CData)); }
void CData::PrintData( ) const
{ cout.precision(3);
cout.setf (ios::showpoint);
cout<<setw(4)<<n<<setw(7)<<f<<setw(4)<<m
<<setw(7)<<x<<setw(8)<<y<<setw(9)<<z<<endl;
}
C/C++程序设计
32
void main(void)
{ CData s = { 0,1,2,3,4,5 }; //定义一个结构变量
CData d[] = { 1,2,3,4,5,6,2,3,4,5,6,7,3,4,5,6,7,8 };
//定义一个结构数组
fstream fpWrite;
fpWrite.open ( "c:\\cdata.dat",ios::out );
if(fpWrite.fail()!=0) { cout<<"open w failed"; return; }
WriteData(&s,1,fpWrite); //将结构变量 s写入磁盘
WriteData( d,3,fpWrite); //将结构数组 d写入磁盘
fpWrite.close(); //及时地进行实际存盘运作
fstream fpRead;
fpRead.open("c:\\cdata.dat",ios::in);
//以缺省方式打开上面刚生成的文件
C/C++程序设计
33
if (fpRead.fail()!=0) { cout<<"open w failed";return; }
CData b[4];
//定义一个有 4个元素的结构数组
ReadData(b,3,fpRead);
//在文件中先读取 3组结构数据直接送入 b[0],b[1],b[2]
ReadData(b+3,1,fpRead);
//在文件中随后位置再读取 1组结构数据直接送入 b[3]
for(int k=0;k<4;k++)
//在屏幕上循环显示每一结构元素的成员值
b[k].PrintData();
//二进制数据通过格式转换以文本输出
}
C/C++程序设计
34
c:\cdata.dat文件的长度为 4*6*4=96个字节,屏幕显示结果为:
0 1.00 2 3.00 4.00 5.00
1 2.00 3 4.00 5.00 6.00
2 3.00 4 5.00 6.00 7.00
3 4.00 5 6.00 7.00 8.00
C/C++程序设计
35
八、文件的定位文件的定位函数在流对象对应的文件中操作,将其中的位置指针变动到新的位置,新的位置由 offset确定。
偏移量 offset指出距离 origin起始位置的字节偏移数,
正的 long型实参表示以起始位置为基准前移 offset个字节作为新的位置,负的实参则后移。
如果未提供 origin起始位置,则操作的起始位置为文件的开始处,相当于 origin= ios:,beg。
C/C++程序设计
36
在 istream类中提供三个定位磁盘位置的函数,它们的函数原型和含义如下:
typedef long streamoff,streampos;
istream& seekg(streampos position);
位置指针移到 position位置
istream& seekg (streamoff offset,ios::seek_dir origin);
相对于 origin位置移动 offset个字节
streampos tellg();
返回指定文件中的当前位置
C/C++程序设计
37
在 ostream类中提供三个定位磁盘位置的函数,它们的函数原型和含义如下:
ostream& seekp (streampos position);
位置指针移到 position位置
ostream& seekp (streamoff offset,ios::seek_dir origin);
相对于 origin位置移动 offset个字节
streampos tellp();
返回指定文件中的当前位置
C/C++程序设计
38
seek_dir是在 ios引入的梅举类名,其中的梅举元素和含义为:
enum seek_dir
{ beg=0,//文件开始位置
cur=1,//文件当前位置
end=2 //文件结束位置
};
C/C++程序设计
39
在 istream类中定位的成员函数带后缀 g,例如,
tellg
在 ostream类中定位的成员函数带后缀 p,例如,
seekp
这是解决多继承的名称歧义而添加的后缀。
梅举类型的形参 origin在 seek_dir类的梅举元素中取值即只能为,
ios:,beg,ios:,cur或 cur:,end
C/C++程序设计
40