C语言:
一 打开关闭文件
1 fopen函数 用于打开文件
FILE *fopen(char *filename, *type);
fopen("c:\ccdos\clib", "rb"); 如果成功的打开一个文件, fopen()函数返回文件指针,否则返回空指针(NULL)。由此可判断文件打开是否成功。
"r" 打开文字文件只读
"w" 创建文字文件只写
"a" 增补,如果文件不存在则创建一个
"r+" 打开一个文字文件读/写
"w+" 创建一个文字文件读/写
"a+" 打开或创建一个文件增补
"b" 二进制文件(可以和上面每一项合用)
"t" 文这文件(默认项)
2. fclose()函数
fclose()函数用来关闭一个由fopen()函数打开的文件,其调用格式为:
int fclose(FILE *stream);该函数返回一个整型数。当文件关闭成功时,返回0,否则返回一个非零值。可以根据函数的返回值判断文件是否关闭成功。
二 有关文件操作的函数
本节所涉及到的文件读写函数均是指顺序读写,即读写了一条信息后,指针自动加1.
下面分别介绍写操作函数和读操作函数。
1. 文件的顺序写函数
fprintf()、fputs()和fputc()函数均为文件的顺序写操作函数,其调用格式如下:
int fprintf(FILE *stream, char *format, <variable-list>);
int fputs(char *string, FILE *steam);
int fputc(int ch, FILE *steam); 上述三个函数的返回值均为整型量。
fprintf() 函数的返回值为实际写入文件中的字符个数(字节数)。如果写错误,则返回一个负数;
fputs()函数返回0时表明将string指针所指的字符串写入文件中的操作成功, 返回非0时,表明写操作失败。
fputc()函数返回一个向文件所写字符的值,此时写操作成功,否则返回EOF(文件结束结束其值为-1, 在stdio.h中定义)表示写操作错误。
fprintf( ) 函数中格式化的规定与printf( ) 函数相同,所不同的只是fprintf()函数是向文件中写入。而printf()是向屏幕输出。
例如:
fputs("Your score of TOEFLis", fp);/*向所建文件写入一串字符*/
fputc(':', fp); /*向所建文件写冒号:*/
fprintf(fp, "%d
", i); /*向所建文件写一整型数*/
fprintf(fp, "%s", s); /*向所建文件写一字符串*/
2. 文件的顺序读操作函数
函数fscanf()、fgets()和fgetc()均为文件的顺序读操作函数,其调用格式如下:
int fscanf(FILE *stream, char *format, <address-list>);
char fgets(char *string, int n, FILE *steam);
int fgetc(FILE *steam);
fscanf()函数的用法与scanf()函数相似,只是它是从文件中读到信息。 fscanf()函数的返回值为EOF(即-1), 表明读错误,否则读数据成功。读到空白字符自动返回。
fgets()函数从文件中读取至多n-1个字符(n用来指定字符数), 并把它们放入string指向的字符串中,在读入之后自动向字符串未尾加一个空字符,读成功返回string指针,失败返回一个空指针。
fgetc()函数返回文件当前位置的一个字符,读错误时返回EOF。
3. 文件的随机读写
有时用户想直接读取文件中间某处的信息, 若用文件的顺序读写必须从文件 头开始直到要求的文件位置再读, 这显然不方便。Turbo C2.0提供了一组文件的 随机读写函数, 即可以将文件位置指针定位在所要求读写的地方直接读写。
文件的随机读写函数如下:
int fseek (FILE *stream, long offset, int fromwhere);
int fread(void *buf, int size, int count, FILE *stream);
int fwrite(void *buf, int size, int count, FILE *stream);
long ftell(FILE *stream);
fseek()函数的作用是将文件的位置指针设置到从fromwhere开始的第offset 字节的位置上, 其中fromwhere是下列几个宏定义之一:
文件位置指针起始计算位置fromwhere
━━━━━━━━━━━━━━━━━━━━━━━━━━━
符号常数 数值 含义
───────────────────────────
SEEK_SET 0 从文件开头
SEEK_CUR 1 从文件指针的现行位置
SEEK_END 2 从文件末尾
━━━━━━━━━━━━━━━━━━━━━━━━━━━
offset是指文件位置指针从指定开始位置(fromwhere指出的位置)跳过的字节数。它是一个长整型量, 以支持大于64K字节的文件。
fseek()函数一般用于对 二进制文件进行操作。当fseek()函数返回0时表明操作成功, 返回非0表示失败。
fread()函数是从文件中读count个字段, 每个字段长度为size个字节, 并把 它们存放到buf指针所指的缓冲器中。
fwrite()函数是把buf指针所指的缓冲器中, 长度为size个字节的count个字 段写到stream指向的文件中去。
随着读和写字节数的增大, 文件位置指示器也增大, 读多少个字节, 文件位 置指示器相应也跳过多少个字节。读写完毕函数返回所读和所写的字段个数。
ftell()函数返回文件位置指示器的当前值,这个值是指示器从文件头开始 算起的字节数, 返回的数为长整型数, 当返回-1时, 表明出现错误。
下面程序把一个浮点数组以二进制方式写入文件test_b.dat中。
例14:
#include <stdio.h>
main()
{
float f[6]={3.2, -4.34, 25.04, 0.1, 50.56, 80.5};
/*定义浮点数组并初始化*/
int i;
FILE *fp;
fp=fopen("test_b.dat", "wb"); /*创建一个二进制文件只写*/
fwrite(f, sizeof(float), 6, fp);/*将6个浮点数写入文件中*/
fclose(fp); /*关闭文件*/
}
下面例子从test_b.dat文件中读100个整型数, 并把它们放到dat数组中。 例15:
#include <stdio.h>
main()
{
FILE *fp;
intdat[100];
fp=fopen("test_b.dat", "rb");/*打开一个二进制文件只读*/
if(fread(dat, sizeof(int), 100, fp)!=100)
/*判断是否读了100个数*/
{
if(feof(fp))
printf("End of file"); /*不到100个数文件结束*/
else
printf("Read error"); /*读数错误*/
fclose(fp); /*关闭文件*/
}
注意:
当用标准文件函数对文件进行读写操作时, 首先将所读写的内容放进缓冲区, 即写函数只对输出缓冲区进行操作, 读函数只对输入缓冲区进行操作。例如向一 个文件写入内容, 所写的内容将首先放在输出缓冲区中, 直到输出缓冲区存满或使用fclose()函数关闭文件时, 缓冲区的内容才会写入文件中。若无fclose()函数, 则不会向文件中存入所写的内容或写入的文件内容不全。有一个对缓冲区 进行刷新的函数, 即fflush(), 其调用格式为:
int fflush(FILE *stream);该函数将输出缓冲区的内容实际写入文件中, 而将输入缓冲区的内容清除掉。
4. feof()和rewind()函数
这两个函数的调用格式为:
int feof(FILE *stream);
int rewind(FILE *stream);
feof()函数检测文件位置指示器是否到达了文件结尾,若是则返回一个非0值, 否则返回0。这个函数对二进制文件操作特别有用, 因为二进制文件中,文件结尾标志EOF也是一个合法的二进制数,只简单的检查读入字符的值来判断文件是否结束是不行的。如果那样的话, 可能会造成文件未结尾而被认为结尾, 所以就必须有feof()函数。
下面的这条语句是常用的判断文件是否结束的方法。
while(!feof(fp))
fgetc(fp);
rewind()函数用于把文件位置指示器移到文件的起点处, 成功时返回0,否则, 返回非0值。
C语言本身并不提供输入输出语句,输入和输出操作是由函数来实现的。在C标准函数库中提供了一些输入输出函数,例如,printf函数和scanf函数。
基于C++的文件操作
在C++中,有一个stream这个类,所有的I/O都以这个“流”类为基础的,包括我们要认识的文件I/O,stream这个类有两个重要的运算符:
1、插入器(<<) 向流输出数据。比如说系统有一个默认的标准输出流(cout),一般情况下就是指的显示器,所以,cout<<"Write Stdout"<<' ';就表示把字符串"Write Stdout"和换行字符(' ')输出到标准输出流。
2、析取器(>>) 从流中输入数据。比如说系统有一个默认的标准输入流(cin),一般情况下就是指的键盘,所以,cin>>x;就表示从标准输入流中读取一个指定类型(即变量x的类型)的数据。
在C++中,对文件的操作是通过stream的子类fstream(file stream)来实现的,所以,要用这种方式操作文件,就必须加入头文件fstream.h。下面就把此类的文件操作过程一一道来。
一、打开文件 在fstream类中,有一个成员函数open(),就是用来打开文件的,其原型是:
void open(const char* filename,int openmode,int access);
参数:
filename: 要打开的文件名 mode: 要打开文件的方式
access: 打开文件的属性 打开文件的方式在类ios(是所有流式I/O类的基类)中定义,常用的值如下:
ios::app: 以追加的方式打开文件 ios::ate: 文件打开后定位到文件尾,ios:app就包含有此属性
ios::binary: 以二进制方式打开文件,缺省的方式是文本方式。两种方式的区别见前文 ios::in: 文件以输入方式打开(文件=>程序) ios::out: 文件以输出方式打开 (程序=>文件) ios::nocreate: 不建立文件,所以文件不存在时打开失败 ios::noreplace:不覆盖文件,所以打开文件时如果文件存在失败
ios::trunc: 如果文件存在,把文件长度设为0 可以用“或”把以上属性连接起来,如ios::out|ios::binary
打开文件的属性取值是:
0:普通文件,打开访问 1:只读文件 2:隐含文件 4:系统文件 可以用“或”或者“+”把以上属性连接起来 ,如3或1|2就是以只读和隐含属性打开文件。
例如:以二进制输入方式打开文件c:config.sys
fstream file1; file1.open("c:\config.sys",ios::binary|ios::in,0);
如果open函数只有文件名一个参数,则是以读/写普通文件打开,即:
file1.open("c:\config.sys");<=>file1.open("c:\config.sys",ios::in|ios::out,0);
另外,fstream还有和open()功能一样的构造函数,对于上例,在定义的时侯就可以打开文件了:
fstream file1("c:\config.sys");
特别提出的是,fstream有两个子类:ifstream(input file stream)和ofstream(outpu file stream),ifstream默认以输入方式打开文件(文件=>程序),而ofstream默认以输出方式打开文件。
ifstream file2("c:\pdos.def");//以输入方式打开文件 ofstream file3("c:\x.123");//以输出方式打开文件
所以,在实际应用中,根据需要的不同,选择不同的类来定义:如果想以输入方式打开,就用ifstream来定义;如果想以输出方式打开,就用ofstream来定义;如果想以输入/输出方式来打开,就用fstream来定义。
二、关闭文件 打开的文件使用完成后一定要关闭,fstream提供了成员函数close()来完成此操作,如:file1.close();就把file1相连的文件关闭。
三、读写文件 读写文件分为文本文件和二进制文件的读取,对于文本文件的读取比较简单,用插入器和析取器就可以了;而对于二进制的读取就要复杂些,下要就详细的介绍这两种方式
1、文本文件的读写 文本文件的读写很简单:用插入器(<<)向文件输出;用析取器(>>)从文件输入。假设file1是以输入方式打开,file2以输出打开。示例如下:
file2<<"I Love You";//向文件写入字符串"I Love You" int i; file1>>i;//从文件输入一个整数值。
这种方式还有一种简单的格式化能力,比如可以指定输出为16进制等等,具体的格式有以下一些
操纵符 功能 输入/输出 dec 格式化为十进制数值数据 输入和输出 endl 输出一个换行符并刷新此流 输出 ends 输出一个空字符 输出 hex 格式化为十六进制数值数据 输入和输出 oct 格式化为八进制数值数据 输入和输出
setpxecision(int p) 设置浮点数的精度位数 输出
比如要把123当作十六进制输出:file1<<<123;要把3.1415926以5位精度输出:file1<<<3.1415926。
2、二进制文件的读写 ①put() put()函数向流写入一个字符,其原型是ofstream &put(char ch),使用也比较简单,如file1.put('c');就是向流写一个字符'c'。
②get() get()函数比较灵活,有3种常用的重载形式:
一种就是和put()对应的形式:ifstream &get(char &ch);功能是从流中读取一个字符,结果保存在引用ch中,如果到文件尾,返回空字符。如file2.get(x);表示从文件中读取一个字符,并把读取的字符保存在x中。
另一种重载形式的原型是: int get();这种形式是从流中返回一个字符,如果到达文件尾,返回EOF,如x=file2.get();和上例功能是一样的。
还有一种形式的原型是:ifstream &get(char *buf,int num,char delim=' ');这种形式把字符读入由 buf 指向的数组,直到读入了 num 个字符或遇到了由 delim 指定的字符,如果没使用 delim 这个参数,将使用缺省值换行符' '。例如:
file2.get(str1,127,'A');//从文件中读取字符到字符串str1,当遇到字符'A'或读取了127个字符时终止。
③读写数据块 要读写二进制数据块,使用成员函数read()和write()成员函数,它们原型如下:
read(unsigned char *buf,int num); write(const unsigned char *buf,int num);
read()从文件中读取 num 个字符到 buf 指向的缓存中,如果在还未读入 num 个字符时就到了文件尾,可以用成员函数 int gcount();来取得实际读取的字符数;而 write() 从buf 指向的缓存写 num 个字符到文件中,值得注意的是缓存的类型是 unsigned char *,有时可能需要类型转换。
例:
unsigned char str1[]="I Love You"; int n[5]; ifstream in("xxx.xxx"); ofstream out("yyy.yyy"); out.write(str1,strlen(str1));//把字符串str1全部写到yyy.yyy中 in.read((unsigned char*)n,sizeof(n));//从xxx.xxx中读取指定个整数,注意类型转换 in.close();out.close();
四、检测EOF 成员函数eof()用来检测是否到达文件尾,如果到达文件尾返回非0值,否则返回0。原型是int eof();
例: if(in.eof())ShowMessage("已经到达文件尾!");
五、文件定位 和C的文件操作方式不同的是,C++ I/O系统管理两个与一个文件相联系的指针。一个是读指针,它说明输入操作在文件中的位置;另一个是写指针,它下次写操作的位置。每次执行输入或输出时, 相应的指针自动变化。所以,C++的文件定位分为读位置和写位置的定位,对应的成员函数是 seekg()和 seekp(),seekg()是设置读位置,seekp是设置写位置。它们最通用的形式如下:
istream &seekg(streamoff offset,seek_dir origin); ostream &seekp(streamoff offset,seek_dir origin);
streamoff定义于 iostream.h 中,定义有偏移量 offset 所能取得的最大值,seek_dir 表示移动的基准位置,是一个有以下值的枚举:
ios::beg: 文件开头 ios::cur: 文件当前位置 ios::end: 文件结尾
这两个函数一般用于二进制文件,因为文本文件会因为系统对字符的解释而可能与预想的值不同。
例:
file1.seekg(1234,ios::cur);//把文件的读指针从当前位置向后移1234个字节 file2.seekp(1234,ios::beg);//把文件的写指针从文件开头向后移1234个字节
-------------------------------------------------------------------------------------------------------------------------------------------------------------
文本文件读写:
#include <fstream> #include <iostream> using namespace std; int main() { //文本文件打开方式设为可读写 // fstream outfile("f1.dat",ios::out|ios::in); // if (!outfile) // { // cout<<"open error!"<<endl; // exit(1);
// }
// char a[10]; // cout<<"输入十个数字"<<endl; // //写入文件中 // for (int i=0;i<10;i++) // { // cin>>a[i]; // outfile<<a[i]<<' '; // } // //从文件读取 // for (int i=0;i<10;i++) // { // outfile>>a[i]; // cout<<a[i]<<' '; // } // outfile.close(); //从键盘读入一行字符,将其中的字母存在磁盘文件 // ofstream outfile("f2.dat"); // if (!outfile) // { // cout<<"open error"<<endl; // exit(1);
// } // char c[80]; // cin.getline(c,80);//最多提取80个字符,或者遇到默认终止符 结束 // for (int i=0;c[i]!=' ';i++) // { // if (c[i]>=65&&c[i]<=90||c[i]>=97&&c[i]<=122) // { // //outfile.put(c[i]);或者 // outfile<<c[i]; // cout<<c[i]; // } // } // cout<<endl; // outfile.close(); //从上面的文件读入字符,将其中的小写字母改写为大写字母在存在f3文件 ifstream infile("f2.dat"); if (!infile) { cout<<"openerrr"; exit(1);
} ofstream outfile3("f3.dat"); if (!outfile3) { cout<<"error"; exit(1);
} char ch; while (infile>>ch/*infile.get(ch)*/) { if (ch>=97&&ch<=122) { ch=ch-32; } outfile3<<ch; cout<<ch; } cout<<endl; infile.close(); outfile3.close(); return 0; }
二进制文件读写:
主要用istream类的成员函数read 和write来实现 ,成员函数原型:
istream& read(char *buffer,int len);
ostream& write(const char *buffer,int len);
字符指针指向内存中一段存储空间,len是读写的字节数。
#include <fstream> #include <iostream> using namespace std; struct student { char name[20]; int num; int age; char sex; }; int main() { student stu[3]={"Li",1001,18,'f',"Fun",1002,19,'f',"Wa",1003,17,'m'}; ofstream outfile("student.dat",ios::binary);//和文件后缀名没关系,即使写成student.txt还是打开乱码,因为编码是自定义的,默认的字符编码解释不了的。 if (!outfile) { cerr<<"open erroe"; exit(1); } // for (int i=0;i<3;i++) // { // //outfile.write((char *)&stu[i],sizeof(stu[i])); // outfile.write((char *)(stu+i),sizeof(stu[i])); // } //取代for循环,直接一句话 outfile.write((char *)stu,sizeof(stu)); outfile.close(); //读取 ifstream infile("student.dat",ios::binary); if (!infile) { cerr<<"open error"; exit(1); } student stu2[3]; infile.read((char*)stu2,sizeof(stu2)); infile.close(); //打印 for (int i=0;i<3;i++) { cout<<stu2[i].name<<endl; cout<<stu2[i].age<<endl; cout<<stu2[i].num<<endl; cout<<stu2[i].sex<<endl; } return 0; }
注意
fstream iofile("student.dat",ios::in|ios::out|ios::binary);
if(!iofile)
{cout<<"open error";
exit(1);} //不知道为何新建一个能输入输出的二进制文件总会出错,二上面例子中新建一个输入输出的文本文件可以。
随机访问二进制数据文件
利用seekg(),seekp等成员函数移动指针,随机的访问文件中任意位置上的数据。
举例:(文件读写有两个指针,读文件指针和写指针。)
iofile.seekg(2*sizeof(stu[2]),ios::beg);把输入指针移动到第三个学生数据开头
iofile.read((char *)&stu2[0],sizeof(stu2[0]));
iofile.seekp(2*sizeof(stu[2]),ios::beg);把输出指针移动到第三个学生数据开头
iofile.write((char *)&stu2[0],sizeof(stu2[0]));