首页 > 代码库 > 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++文件读写操作总结