首页 > 代码库 > C/C++文件读写操作总结

C/C++文件读写操作总结

本文主要从两方面介绍读写文件操作,一个是C,另一个是C++。

一、基于C的文件操作。

在ANSI C中对文件操作有两种方式,一种是流式文件操作,另一种是I/O文件操作。下面分别介绍。

1.流式文件操作。

流式文件操作有一个重要的结构FILE, FILE是在stdio.h中定义:

typedef struct {    int level;    unsigned flags;    char fd;    unsigned char hold;    int bsize;    unsigned char _FAR *buffer;    unsigned char _FAR *curp;    unsigned istemp;    short token;} FILE;

FILE这个结构包含了文件操作的基本属性,对文件的操作都要通过这个结构的指针来进行,此种文件常用函数如下:

fopen() 打开流fclose() 关闭流fputc() 写一个字符到流中fgetc() 从流中读一个字符fseek() 在流中定位到指定的字符fputs() 写字符串到流fgets() 从流中读一行或指定个字符fprintf() 按格式输出到流fscanf() 从流中按格式读取feof() 到达文件尾时返回真值ferror() 发生错误时返回其值rewind() 复位文件定位器到文件开始处remove() 删除文件fread() 从流中读指定个数的字符fwrite() 向流中写指定个数的字符tmpfile() 生成一个临时文件流tmpnam() 生成一个唯一的文件名

下面就介绍下这些函数。

(1)fopen()

fopen()的原型是: FILE *fopen(const char * filename, const char* mode),它主要是实现三个功能:打开一个流、连接一个文件与此流、给此流返回一个FILE指针。这里参数filename是指向要打开的文件名,mode表十打开状态的字符串,取值如下:

"r" 以只读方式打开文件"w" 以只写方式打开文件"a" 以追加方式打开文件"r+" 以读/写方式打开文件,如无文件则出错"w+" 以读/写方式打开文件,如无文件则生成新文件

一个文件可以以文本模式或者二进制模式打开,两种的区别是:文本模式中回车会被当成字符“\n”,二进制模式则会认为他是两个字符0x0D 和 0x0A,如果在文件中读到0x1B,文本模式会认为这是文件结束符,二进制模式不会对其进行处理,二本方式按一定的方式对数据作相应转换。

系统默认以文本模式打开,可以修改全局变量_fmode的值来修改这个设置,_fmode=0_TEXT就设置默认打开方式为文本模式,_fmode=0_BINARY则设置默认打开方式为二进制模式。

也可以在模式字符串中指定打开的模式,如“rb”表示二进制模式打开只读文件,“w+t”或“wt+”表示以文本模式打开读写文件。

fopen返回的是一个FILE指针,所以声明一个FILE指针后不用初始化,而是用fopen()来返回一个指针并与一个特定文件相连。失败则返回NULL。如:

1    FILE *fp;2   if(fp=fopen("123.456","wb"))3     puts("打开文件成功");4   else5     puts("打开文件成败");

(2)fclose()。

fclose函数的功能就是关闭fopen打开的文件,原型为int fclose(FILE *fp),成功则返回0,失败则返回EOF。 

程序结束时要记得关闭打开的文件,否则可能造成数据丢失的情况!如

 fclose(fp);

 

(3)fputc()

该函数的功能是向流写一个字符,原型是 int fputc(int c, FILE * stream),成功则返回字符,失败返回EOF。如

fputc(X,fp);

(4)fgetc()

从流中读取一个字符,原型是int fputc(FILE *stream),成功则返回这个字符,失败则返回EOF。如

char ch1=fgetc(fp);

(5)fseek()

此函数一般用于二进制模式打开的文件中,功能是定位到六中指定的位置,原型是int fseek(FILE *stream, long offset, int whence),成功则返回0,参数offset是移动的字符数,whence是移动的基准,取值是

SEEK_SET 0 文件开头SEEK_CUR 1 当前读写的位置SEEK_END 2 文件尾部

例如,

fseek(fp,1234L,SEEK_CUR);//把读写位置从当前位置向后移动1234字节(L后缀表示长整数)fseek(fp,0L,2);//把读写位置移动到文件尾

(6)fputs()

写一个字符串到流中,原型是int fputs(const char*s, FILE* stream);返回值为0表示成功。写入的字符串也是以结束符‘\n‘为结束的,所以多行写入需要重复操作。如

fputs("I Love You",fp);

(7)fgets()

从流中读取一行或指定个数字符,原型是char *fgets(char *buf, int n, FILE * fp),从流中读取n-1个字符,除非读完一行,这里buf为存储字符串的地址,n为读取字符串的长度,n需>1,否则无法正确返回,fp为文件指针,返回值为空指针是表示获取失败或结束。该函数一次最多只读取一行,遇到‘\n‘就停止,若有多行,需循环读取。

如:文件当前位置为

Love ,I have

but...

如果用fgets(str1, 4, file)则执行后str1=“Lov”,读取了4-1=3个字符,而如果用fgets(str1,30,file)则str1=“Love , I have”,读取了一行,不包括‘\n’以及下一行。

(8)fprintf()

按格式输入到流,原型是int fprintf(FILE * stream, const char* format[,argument, ……]);根据指定的format发送信息(参数)到由stream指定的流中。这里其用法与printf()相同,不过不是写到控制台而是写到流中,出错时返回一个负值。如

fprintf(fp,"-%s",4,"Hahaha");

(9)fscanf()

从流中按格式读取,原型是int fscanf(FILE* stream, const char * format[,address,……]),用法同scanf不过是从流中读取。如

fscanf(fp,"%d%d" ,&x,&y);

(10)feof()

检测是否到文件尾,是则返回真,否则返回0,原型是int feof(FILE *stream);如

if(feof(fp))printf("已到文件尾");

(11)ferror()

原型是int ferror(FILE * stream)返回流最近的错误代码,可用clearerr()来清除它,clearerr原型是void clearerr(FILE* stream);如果ferror返回值为0(假),表示未出错。如果返回一个非零值,表示出错。

应该注意,对同一个文件 每一次调用输入输出函数,均产生一个新的ferror函 数值,因此,应当在调用一个输入输出函数后立即检 查ferror函数的值,否则信息会丢失。在执行fopen函数时,ferror函数的初始值自动置为0。

printf("%d",ferror(fp));

(12)rewind()

把当前的读写位置回到文件开始,原型是void rewind(FILE *stream);其实本函数相当于fseek(fp,0L,SEEK_SET);如

rewind(fp);

(13)remove()

删除文件,原型是int remove(const char *filename); 参数就是要删除的文件名,成功返回0。如

remove("c:\\io.sys");

(14)fread()

从流中读指定个数的字符,原型是size_t fread(void *ptr, size_t size, size_t n, FILE *stream);参数ptr是保存读取的数据,void*的指针可用任何类型的指针来替换,如char*、int *等等来替换;size是每块的字节数;n是读取的块数,如果成功,返回实际读取的块数(不是字节数),本函数一般用于二进制模式打开的文件中。如

   char x[4230];  FILE *file1=fopen("c:\\msdos.sys","r");  fread(x,200,12 ,file1);//共读取200*12=2400个字节

(15)fwrite()

与fread对应,向流中写指定的数据,原型是size_t fwrite(const void *ptr, size_t size, size_t n, FILE *stream);参数ptr是要写入的数据指针,void*的指针可用任何类型的指针来替换,如char*、int *等等来替换;size是每块的字节数;n是要写的块数,如果成功,返回实际写入的块数(不是字节数),本函数一般用于二进制模式打开的文件中。如

   char x[]="I Love You";  fwire(x, 6,12,fp);//写入6*12=72字节

将把"I Love"写到流fp中12次,共72字节

(16)temfile()

其原型是FILE *tmpfile(void); 生成一个临时文件,以"w+b"的模式打开,并返回这个临时流的指针,如果失败返回NULL。在程序结束时,这个文件会被自动删除。如

FILE *fp=tmpfile();

(17)tmpnam()

其原型为char *tmpnam(char *s); 生成一个唯一的文件名,其实tmpfile()就调用了此函数,参数s用来保存得到的文件名,并返回这个指针,如果失败,返回NULL。如

tmpnam(str1);

 

以上就是对于流式文件操作的基本函数总结,用的比较多的是前几个,后面的在我目前的水平以及接触的东西来说基本没用到,仅供参考。

 

2.基于I/O文件操作。

这是C提供的另一种文件操作,它是通过直接存/取文件来完成对文件的处理。上面的流式文件操作是通过缓冲区来进行的。文件操作是围绕一个文件的句柄进行操作的。句柄是一个整数,是系统用来标识一个文件的唯一记号。此类文件操作常用的函数如下,

open() 打开一个文件并返回它的句柄close() 关闭一个句柄lseek() 定位到文件的指定位置read() 块读文件write() 块写文件eof() 测试文件是否结束filelength() 取得文件长度rename() 重命名文件chsize() 改变文件长度

下面进行说明。

(1)open()函数

打开一个文件并返回他的句柄,如果失败,返回一个小于0的值,原型为int open(const char* path, int access[,unsigned mode]);参数path是要打开的文件名,access是打开的模式,mode是可选项,表示文件属性。文件打开模式如下,

O_RDONLY 只读方式 O_WRONLY 只写方式 O_RDWR 读/写方式O_NDELAY 用于UNIX系统 O_APPEND 追加方式 O_CREAT 如果文件不存在就创建O_TRUNC 把文件长度截为0 O_EXCL 和O_CREAT连用,如果文件存在返回错误 O_BINARY 二进制方式O_TEXT 文本方式

对于多个要求的,可以用“|”来连接。如

int handle=open("c:\\msdos.sys",O_BINARY|O_CREAT|O_WRITE)

(2)close

关闭一个句柄,原型是int close(int handle);如果成功则返回0.如

close(handle)

(3)lseek()

定位到指定位置,原型是long lseek(int handle, long effect, int fromwhere),参数offset是移动的量,fromwhere是移动的基准位置,取值和前面的fseek()一样,SEEK_SET:文件首部,SEEK_CUR文件当前位置,SEEK_END  文件结尾。此函数返回执行后文件新的存储位置。如:

lseek(handle,-1234L,SEEK_CUR);//把存取位置从当前位置向前移动1234个字节。x=lseek(hnd1,0L,SEEK_END);//把存取位置移动到文件尾,x=文件尾的位置即文件长度

(4)read

从文件读取一块,原型是int read(int handle, void * buf, unsigned len);参数buf保存读取的数,len是读取的字节,函数返回实际取出的字节。如

char x[200];
read(hnd1,x,200);

(5)write()

写一块数据到文件中,原型是int write(int handle, void*buf, unsigned len),参数含义同read函数,返回实际写入的字节。如

char x[]="I Love You";
write(handle,x,strlen(x));

(6)eof()

类似feof(),测试文件是否结束,是则会返回1,否则返回0.原型是int eof(int handle);如

while(!eof(handle1)){    ……}

(7)filelength()

返回文件长度,原型是long filelength(int handle)相当于lseek(handle, 0,SEEK_END)。如

long x=filelength(handle);

(8)rename()

重命名文件,原型是int rename(const char* oldname, const char* newname),成功则返回0.如

rename("c:\\config.sys","c:\\config.w40");

(9)chsize()

改变文件长度,原型是int chsize(int handle, long size)成功则返回0,否则返回-1.如果指定的长度大于文件长度,则在文件后面补‘\0’.

 

这里面同样有很多是不常用的,最起码对于我来说没咋接触,不过还是要了解下,方便以后的学习与交流。

 

以上就是在C中的关于文件读写的操作,总结下就是分为两类,流式读写和I/O读写。

 

二、下面再说下C++中的stream类。所有的I/O都以这个流类作为基础,包括我们要认识的文件I/O,stream这个类有两种重要运算符:

1.插入器(<<)

向流输入数据,比如系统有一个默认的标准输出流(cout),一般情况下就是只显示器,所以cout<< "hello world"<<"\n";就是把字符串hello world 和换行字符输出到标准输出流。

2.析取器(>>)

从流中输入数据,比如说系统有一个默认的标准输入流(cin)一般情况下就是键盘,所以cin>>x就是从标准输入流中读取一个指定类型的数据。

C++中对文件的操作是通过stream的子类fstream(file stream)来实现的,所以,要用这种方式操作文件必须要加入头文件fsteam.h。

 

操作方法如下。

1.打开文件open。

原型为void open(const char* filename, int mode, int access);参数filename是要打开的文件名,mode是打开的方式,access是打开文件的属性。

打开文件的方式是在ios类中定义,常用如下:

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就是以只读和隐含属性打开文件。

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有两个子类:ifstream(input file stream)和ofstream(output file stream),ifstream默认以输入方式打开文件,而ofstream默认以输出方式打开文件。

所以,在实际应用中,根据需要的不同,选择不同的类来定义:如果想以输入方式打开,就用ifstream来定义;如果想以输出方式打开,就用ofstream来定义;如果想以输入/输出方式来打开,就用fstream来定义。

2.关闭文件

打开的文件使用完成后一定要关闭,fstream提供了成员函数close()来完成此操作,如:file1.close();就把file1相连的文件关闭。

3.读写文件

读写文件分为文本文件和二进制文件的读取,对于文本文件的读取比较简单,用插入器和析取器就可以了;而对于二进制的读取就要复杂些,下要就详细的介绍这两种方式

(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<<hex<<123;要把3.1415926以5位精度输出:file1<<setpxecision(5)<<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=‘‘\n‘‘);这种形式把字符读入由 buf 指向的数组,直到读入了 num 个字符或遇到了由 delim 指定的字符,如果没使用 delim 这个参数,将使用缺省值换行符‘‘\n‘‘。例如:

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();

 

4.检测eof

成员函数eof()用来检测是否到达文件尾,如果到达文件尾返回非0值,否则返回0。原型是int eof();如:

if(in.eof())ShowMessage("已经到达文件尾!");

 

5.文件定位

和C的文件操作方式不同的是,C++ I/O系统管理两个与一个文件相联系的指针。一个是读指针,它说明输入操作在文件中的位置;另一个是写指针,它下次写操作的位置。每次执行输入或输出时,相应的指针自动变化。所以,C++的文件定位分为读位置和写位置的定位,对应的成员函数是 seekg()和 seekp(),seekg()是设置读位置,seekp是设置写位置。它们最通用的形式如下:

1 istream &seekg(streamoff offset,seek_dir origin);2 ostream &seekp(streamoff offset,seek_dir origin);

streamoff定义于 iostream.h 中,定义有偏移量 offset 所能取得的最大值,seek_dir 表示移动的基准位置,是一个有以下值的枚举:

1 ios::beg:  文件开头2 ios::cur:  文件当前位置3 ios::end:  文件结尾

 

 

综上,就是C++中对文件的操作,大致还是用的比较多的。

 

总结,本文主要是写了C和C++中对文件的操作方法。内容较多,但都比较简单,容易理解,具体还是要在实际应用中去熟练使用。

C/C++文件读写操作总结