2、文件I/O
1)文件的读写操作
对于文件的I/O 操作可分为按文本(text)方式和按二进制方式两种。
A)文本形式(ASCII)
如果文件的每一个字节中均以ASCII代码形式存放数据,即一个字节存放一个字符,这个文件就是ASCII文件(或称字符文件)。程序可以从ASCII文件中读入若干个字符,也可以向它输出一些字符。
对ASCII文件的读写操作可以用以下两种方法:
(1) 用流插入运算符“<<”和流提取运算符“>>”输入输出标准类型的数据。
(2) 用文件流的put,get,geiline等成员函数进行字符的输入输出。
float size=45.8;
ofstream file1("size.dat");
file1<<"The size is"<<size;
file1.close();
char * str1, *str2, *str3;
float a;
ifstream file2("size.dat");
file2>>str1>>str2>>str3;
file2>>a;
cout<<str1<<str2<<str3<<a<<endl;
注意:在编译上述程序中,发现在MinGw环境中是可以通过的,但是结果不对,如果char *str1,*str2变成string str1,str2则结果是正确的。
#include <fstream>
#include <iostream>
#include <string>
using namespace std;
int main(){
string str1,str2;
fstream file3("hello.dat",ios::out|ios::in);
if(!file3)
{
cerr<<"open error"<<endl;
exit(1);
}
file3<<"aa bb";
file3.seekg(0,ios::beg);
file3>>str1>>str2;
cout<<str1<<endl<<str2<<endl;
file3.close();
return 1;
}
#include <iostream>
#include<fstream>
using namespace std;
int main()
{
int a[10];
ofstream outfile("f1.dat",ios::out);
if(!outfile)
{
cerr<<"open error"<<endl;
exit(1);
}
cout<<"input 10 integer numbers:"<<endl;
for(int i=0;i<10;i++)
{
cin>>a[i];
outfile<<a[i]<<" ";
}//for
outfile.close();
return 1;
}
#include <iostream>
#include<fstream>
using namespace std;
int main()
{
int a[10];
ifstream infile("f1.dat",ios::in);
if(!infile)
{
cerr<<"open error!"<<endl;
exit(1);
}//if
for(int i=0;i<10;i++)
{
infile>>a[i];
cout<<a[i]<<" ";
}//for
cout<<endl;
infile.close();
return 1;
}
B)二进制形式
二进制文件不是以ASCII代码存放数据的,它将内存中数据存储形式不加转换地传送到磁盘文件,因此它又称为内存数据的映像文件。因为文件中的信息不是字符数据,而是字节中的二进制形式的信息,因此它又称为字节文件。
主要的二进制读写函数由类istream 和ostream 提供:
读(输入)函数:
istream&get(unsigned char&);
istream&read(unsigned char *,int);
写(输出)函数:
ostream&put(unsigned char&);
ostream&write(unsigned char *,int); //int 指明读写字节数
get()和put()每次调用读入或写出一个字符。虽然它们的定义在istream 类和ostream 类中,但常常是用在其派生类ifstream 和ofstream 类的对象流的I/O 操作。其用法如下例所示:
#include<fstream.h>
void main(){
static char str[]="Hello world!";
ofstream fout("hello.dat");
int i=0;
while(str[i])fout.put(str[i++]);
fout.close();
}
这个程序把字符串”Hello world!”包括空格和标点全部送到了文件hello.dat中。反之,把它读入并显示出来时,也不必考虑其文本格式。
#include<fstream.h>
void main(){
char ch;
ifstream fin("hello.dat");
while(fin.eof()){
fin.get(ch);
cout<<ch;
};
fin.close();
}
从例中可以看出,get(),put()函数,对文件进行读、写十分简单,以每次
一字节(8 位),顺序进行,文件中的各种特殊字符如空格,换行符等,它也一
律按普通字符处理。
使用read(),write()函数代替get()和put(),可以一次完成读写操作,例如在上面的例子中我们使用write()代替put()时,其中的语句:
int i=0;
while(str[i])fout.put(str[i++]);
可用下面的语句取代:
fout.write(str,sizeof(str));
在输入的情形中,可把程序改写为:
# include<fstream.h>
void main(){
char str[20];
ifstream fin("hello.dat");
fin.read(str,13);
fin.close();
cout<<str;
}
对二进制文件的操作也需要先打开文件,用完后关闭。在打开时要用ios::binary指定为以二进制形式传送和存储。二进制文件除了可以作为输入文件或输出文件外,还可以是既能输入又能输出的文件。这是和ASCII文件不同的地方。举例如下
#include <fstream>
#include <iostream>
#include <string>
using namespace std;
struct student
{
string name;
int num;
int age;
char sex;
};
int main(){
student stud[2]={{"Li",1001,18,'F'},{"Lei",1002,19,'M'}},stud1[2];
ofstream outfile("stud.dat",ios::binary);
if(!outfile) {cerr<<"open error"<<endl;exit(1);}
outfile.write((char*)&stud[0],sizeof(stud));
outfile.close( );
ifstream infile("stud.dat",ios::binary);
if(!infile) {cerr<<"open error"<<endl;exit(1);}
infile.read((char*)&stud1[0],sizeof(stud1));
//for(int i=0;i<2;i++)
//infile.read((char*)&stud[i],sizeof(stud[i]));
infile.close( );
for(int i=0;i<2;i++)
{cout<<"NO."<<i+1<<endl;
cout<<"name:"<<stud1[i].name<<endl;
cout<<"num:"<<stud1[i].num<<endl;;
cout<<"age:"<<stud1[i].age<<endl;
cout<<"sex:"<<stud1[i].sex<<endl<<endl;
}
return 1;
}
在磁盘文件中有一个文件指针,用来指明当前应进行读写的位置。对于二进制文件,允许对指针进行控制,使它按用户的意图移动到所需的位置,以便在该位置上进行读写。
为了方便二进制方式的读写,系统提供了设置和读取文件的读写指针位置的函数。其中由类istream 提供函数成员:
long tellg();
istream&seekg(long offset,int dir=ios::beg);
由类ostream 提供了函数成员:
long tellp();
ostream&seekp(long offset;int dir=ios::beg);
其中参数offset 给出一整数,表示相对偏移字节数,参数dir 有三个取值(在ios 类中说明):
enum seek_dir{
beg=0; //相对于文件开始位置
cut=1; //相对于指针当前位置
end=2; //相对于文件尾的位置
};
由第二个参数决定offset 的数是从哪里开始计算的,参数dir 有缺省值为
ios::beg。
它们的使用方法:
long pos=fout.tellp();把文件流fout 的写指针的当前位置取出送到变量pos。
fin.seekg(0,ios::beg);把文件流fin 的读指针定位到文件开头。
举例如下:
infile.seekg(100);//输入文件中的指针向前移到100字节位置
infile.seekg(-50,ios::cur); //输入文件中的指针从当前位置后移50字节
outfile.seekp(-75,ios::end); //输出文件中的指针从文件尾后移50字节
同一磁盘文件在程序中需要频繁地进行输入和输出,因此可将文件的工作方式指定为输入输出文件,即ios::in|ios::out|ios::binary。
for(int i=0;i<5;i=i+2)
{iofile.seekg(i*sizeof(stud[i]),ios::beg); //定位于第0,2,4学生数据开头
iofile.read((char *)&stud1[i/2],sizeof(stud1[0]));
//先后读入3个学生的数据,存放在stud1[0],stud[1]和stud[2]中
iofile.seekp(2*sizeof(stud[0]),ios::beg); //定位于第3个学生数据的开头
iofile.write((char *)&stud[2],sizeof(stud[2])); //更新第3个学生数据
iofile.seekg(0,ios::beg); //重新定位于文件开头
3、字符串流
字符串流以内存中用户定义的字符数组(字符串)为输入输出的对象,即将数据输出到内存中的字符数组,或者从字符数组(字符串)将数据读入。字符串流也称为内存流。字符串流也有相应的缓冲区,开始时流缓冲区是空的。如果向字符数组存入数据,随着向流插入数据,流缓冲区中的数据不断增加,待缓冲区满了(或遇换行符),一起存入字符数组。如果是从字符数组读数据,先将字符数组中的数据送到流缓冲区,然后从缓冲区中提取数据赋给有关变量。
在字符数组中可以存放字符,也可以存放整数、浮点数以及其他类型的数据。在向字符数组存入数据之前,要先将数据从二进制形式转换为ASCII代码,然后存放在缓冲区,再从缓冲区送到字符数组。从字符数组读数据时,先将字符数组中的数据送到缓冲区,在赋给变量前要先将ASCII代码转换为二进制形式。总之,流缓冲区中的数据格式与字符数组相同。
而字符串流类有istrstream,ostrstream和strstream。文件流类和字符串流类都是ostream,istream和iostream类的派生类,因此对它们的操作方法是基本相同的。但有区别:
(1) 输出时数据不是流向外存文件,而是流向内存中的一个存储空间。输入时从内存中的存储空间读取数据。
(2) 字符串流对象关联的不是文件,而是内存中的一个字符数组,因此不需要打开和关闭文件。
(3) 每个文件的最后都有一个文件结束符,表示文件的结束。而字符串流所关联的字符数组中没有相应的结束标志,用户要指定一个特殊字符作为结束符,在向字符数组写入全部数据后要写入此字符。
字符串流类没有open成员函数,因此要在建立字符串流对象时通过给定参数来确立字符串流与字符数组的关联。即通过调用构造函数来解决此问题。
1)建立输出字符串流对象
ostrstream类提供的构造函数的原型为
ostrstream::ostrstream(char *buffer,int n,int mode=ios::out);
buffer是指向字符数组首元素的指针,n为指定的流缓冲区的大小(一般选与字符数组的大小相同,也可以不同),第3个参数是可选的,默认为ios::out方式。可以用以下语句建立输出字符串流对象并与字符数组建立关联:
ostrstream strout(ch1,20);
作用是建立输出字符串流对象strout,并使strout与字符数组ch1关联(通过字符串流将数据输出到字符数组ch1),流缓冲区大小为20。
2)建立输入字符串流对象
istrstream类提供了两个带参的构造函数,原型为
istrstream::istrstream(char *buffer);
istrstream::istrstream(char *buffer,int n);
buffer是指向字符数组首元素的指针,用它来初始化流对象(使流对象与字符数组建立关联)。可以用以下语句建立输入字符串流对象:
istrstream strin(ch2);
作用是建立输入字符串流对象strin,将字符数组ch2中的全部数据作为输入字符串流的内容。
istrstream strin(ch2,20);
流缓冲区大小为20,因此只将字符数组ch2中的前20个字符作为输入字符串流的内容。
3)建立输入输出字符串流对象
strstream类提供的构造函数的原型为
strstream::strstream(char *buffer,int n,int mode);
可以用以下语句建立输入输出字符串流对象:
strstream strio(ch3,sizeof(ch3),ios::in|ios::out);
作用是建立输入输出字符串流对象,以字符数组ch3为输入输出对象,流缓冲区大小与数组ch3相同。
以上3个字符串流类是在头文件strstream中定义的,因此程序中在用到istrstream, ostrstream和strstream类时应包含头文件strstream。
#include <fstream>
#include <iostream>
#include <string>
#include <strstream>
using namespace std;
int main()
{
char c[50]="12 34 65 -23 -32 33 61 99 321 32";
int a[10],i,j,t;
cout<<"array c:"<<c<<endl;
istrstream strin(c,sizeof(c));
for(i=0;i<10;i++)
strin>>a[i];
cout<<"array a:";
for(i=0;i<10;i++)
cout<<a[i]<<" ";
cout<<endl;
for(i=0;i<9;i++) //用起泡法对数组a排序
for(j=0;j<9-i;j++)
if(a[j]>a[j+1])
{t=a[j];a[j]=a[j+1];a[j+1]=t;}
ostrstream strout(c,sizeof(c));
for(i=0;i<10;i++)
strout<<a[i]<<" ";
strout<<ends;
cout<<" array c:"<<c<<endl;
return 1;
}