文件操作函数
文件的打开和关闭
文件在读写之前应该先打开文件,在使用结束之后应该关闭文件。
在编写程序的时候,在打开文件的同时,都会返回一个FILE*的指针变量指向该文件,也相当于建立了指针和文件 的关系。
ANSIC 规定使用fopen函数来打开文件,fclose来关闭文件。
打开方式如下:
FILE * fopen ( const char * filename, const char * mode );
fopen(文件路径,文件使用方式);
fopen函数打开filename指定的文件,返回一个指向FILE类型的指针,无论使用哪种方式,当打开文件时出现了错误,fopen函数都将返回NULL
| 文件使用方式 | 含义 | 如果指定文件不存在 |
|---|---|---|
| “r”(只读) | 为了输入数据,打开一个已经存在的文本文件 | 该文件必须已经存在,若文件不存在,则会出错 |
| “w”(只写) | 为了输出数据,打开一个文本文件 | 若文件存在则文件长度清为0,即该文件内容会消失。若文件不存在则建立该文件 |
| “a”(追加)(只写) | 向文本文件尾添加数据 | 位置指针移到文件末尾,向文件尾部添加数据,原文件数据保留,若文件不存在则会出错 |
| “rb”(只读) | 为了输入数据,打开一个二进制文件 | 出错 |
| “wb”(只写) | 为了输出数据,打开一个二进制文件 | 建立一个新的文件 |
| “ab”(追加) | 向一个二进制文件尾添加数据 | 出错 |
| “r+”(读写) | 为了读和写,打开一个文本文件 | 出错 |
| “w+”(读写) | 为了读和写,建议一个新的文件 | 建立一个新的文件 |
| “a+”(读写) | 打开一个文件,在文件尾进行读写 | 建立一个新的文件 |
| “rb+”(读写) | 为了读和写打开一个二进制文件 | 出错 |
| “wb+”(读写) | 为了读和写,新建一个新的二进制文件 | 建立一个新的文件 |
| “ab+”(读写) | 打开一个二进制文件,在文件尾进行读和写 | 建立一个新的文件 |
读写权限和读写方式可以组合使用,但是必须将读写方式放在读写权限的中间或者尾部(换句话说,不能将读写方式放在读写权限的开头)
关闭方式如下:
int fclose ( FILE * stream );
函数原型为:_CRTIMP int __cdecl fclose(FILE *);
关闭成功返回值0,否则返回非零值。
备注:
- 在执行完文件的操作后,要进行“关闭文件”操作。虽然程序在结束前会自动关闭所有的打开文件,但文件打开过多会导致系统运行缓慢,这时就要自行手动关闭不再使用的文件,来提高系统整体的执行效率。
- 所下达的读取或写入动作,都是针对缓冲区进行存取而不是磁盘,只有当使用fclose()函数关闭文件时,缓冲区中的数据才会写入磁盘(因为操作系统对于同时打开的文件数目是有限制的,所以在文件使用结束后必须使用fclose关闭文件,否则会出现意想不到的错误)
- 对相对路径而言,路径表示中的"\" 也要用双斜线"\",
c++中\\是一种转义字符,他表示一个\,就像\n表示回车一样。
所以C++中的路径名:
D:\matcom45\doc\users_themes\m.dat
应为:
CString filename=_T(“D:\matcom45\doc\users\_themes\m.dat”);或
CStringfilename=_T("D:/matcom45/doc/users/_themes/m.dat”);
文件的顺序读写
| 功能 | 函数名 | 适用于 |
|---|---|---|
| 字符输入函数 | fgetc | 所有输入流 |
| 字符输出函数 | fputc | 所有输出流 |
| 文本行输入函数 | fgets | 所有输入流 |
| 文本行输出函数 | fputs | 所有输出流 |
| 格式化输入函数 | fscanf | 所有输入流 |
| 格式化输出函数 | fprintf | 所有输出流 |
| 二进制输入 | fread | 文件 |
| 二进制输出 | write | 文件 |
字符存取函数
fgetc函数原型为:
_CRTIMP int __cdecl fgetc(FILE *);
字符读取函数fgetc()可从文件数据流中一次读取一个字符,然后读取光标移动到下一个字符,并逐步将文件的内容读出。
如果字符读取成功,则返回所读取的字符,否则返回EOF(end of file)。EOF是表示数据结尾的常量,真值为-1。
另外,要判断文件是否读取完毕,可利用feof()进行检查。未完返回0,已完返回非零值。
feof()函数原型为:
_CRTIMP int __cdecl feof(FILE *);
fgetc()函数的使用
fgetc()可从文件数据流中一次读取一个字符,然后读取光标移动到下一个字符,并逐步将文件的内容读出
- 利用feof()函数检查文件是否读取完毕
#include <stdio.h>
int main(){
FILE *fp;
fp = fopen("c:\\temp\\test.txt", "r");
if(fp != NULL)
{
while(!feof(fp))
printf("%c", fgetc(fp));
}
else
printf("fail to open! \n");
fclose(fp);
return 0;
}
- 利用文件结束标志EOF(即-1)
#include <stdio.h>
int main(){
char ch;
FILE *fp;
fp = fopen("c:\\temp\\test.txt", "r");
if(fp != NULL)
{
ch = fgetc(fp);
while(ch != EOF)
{
putchar(ch);
ch = fgetc(fp);
}
}
else
printf("fail to open! \n");
fclose(fp);
return 0;
}
- 重构版本2
#include <stdio.h>
int main(){
char ch;
FILE *fp;
if((fp = fopen("test.txt", "r")) != NULL)
while((ch = fgetc(fp)) != EOF)
putchar(ch);\
else
printf("fail to open! \n");
fclose(fp);
return 0;
}
fputc()函数的使用
fputc函数原型为:
_CRTIMP int __cdecl fputc(int, FILE *)
若要将字符逐一写入文件,用fputc()函数。
#include <stdio.h>
#include <conio.h>
int main()
{
char filename[20], ch;
FILE *fp;
printf("Enter a filename: ");
scanf("%s", filename);
getchar();//用于吃掉回车键
printf("Enter some characters to output to file: ");
if ((fp = fopen(filename, "w")) == NULL)
printf("fail to open! \n");
else
{
while ((ch = getchar()) != '\101')
fputc(ch, fp);
}
fclose(fp);
return 0;
}
字符串存取函数
fgets函数原型为:
_CRTIMP char * __cdecl fgets(char *, int, FILE *);
fgets函数的作用是从指定文件读入一个字符串
如:fgets(str, n, fp);
参数n为要求得到的字符个数,但只从fp指向的文件输入n-1个字符,然后在最后加一个’\0’字符,因此得到的字符串共有n个字符,把它们放在字符数组str中。
如果在读完n-1个字符之前遇到回车换行符或EOF,读入结束。
函数返回该字符串的首地址,即指针str的值,读取失败返回空指针NULL
(与gets()不同的是,fgets()从指定的流读取字符串,读到换行符时将换行符也作为字符串的一部分读到字符串中来)
fputs函数原型为:
_CRTIMP int __cdecl fputs(const char *, FILE *);
fputs函数的作用是向指定文件输出一个字符串
如:fputs(“Hey”, fp);
把字符串"Hey"输出到fp指向的文件。fputs函数的第一个参数可以是字符串常量、字符数组名或字符型指针。
若输出成功,则返回0,否则返回EOF。
(与puts()不同的是,fputs()不会在写入文件的字符串末尾加上换行符’\n’)
格式化存取函数
它们与printf和scanf函数相仿,都是格式化读写函数。不同的是:fprintf和fscanf函数的读写对象不是终端(标准输入输出),而是磁盘文件。printf函数是将内容输出到终端(屏幕),因此,fprintf就是将内容输出到磁盘文件了。
fscanf函数:
函数原型:int fscanf (FILE *fp, const char *format, ……);
fscanf(文件指针, 格式字符, 输入列表);
第一个参数为文件指针,第二个参数为格式控制符,第三个参数为地址参数表列,后两个参数与函数scanf()的参数相同
fprintf()函数:
函数原型:int fprintf (FILE *fp, onst char *format, ……);
fprintf(文件指针, 格式字符, 输出列表);
第一个参数为文件指针,第二个参数为格式控制参数,第三个参数为输出参数列表,后两个参数和返回值与函数printf()相同
(用函数fscanf()和fprintf()进行文件的格式化读写,读写方便容易理解,但输入时要将ASCII字符转换成二进制数,输出时要将二进制数转换为ASCII字符,耗时较多)
fprintf和fscanf函数的使用:
#include <stdio.h>
void main()
{
FILE *fp;
int num = 10;
char name[10] = "Leeming";
char gender = 'M';
if((fp = fopen("info.txt", "w+")) == NULL)
printf("can't open the file! \n");
else
fprintf(fp, "%d, %s, %c", num, name, gender); //将数据格式化输出到文件info.txt中
fscanf(fp, "%d, %s, %c", &num, name, &gender); //从文件info.txt中格式化读取数据
printf("%d, %s, %c \n", num, name, gender); //格式化输出到屏幕
fclose(fp);
}
文件的读写
fread()函数
函数原型:unsigned int fread(void *buffer, unsigned int size, unsigned int count, FILE *fp);
函数功能:从fp所指的文件中读取数据块并存储到buffer所指向的内存中,buffer是待读入数据块存储的起始地址,size是每个数据块的大小(待读入的每个数据块的字节数),count是最多允许读取的数据块个数(每个数据块size个字节),函数返回的是实际读到的数据块个数
fwrite()函数
函数原型:unsigned int fwrite(const void *buffer, unsigned int size, unsigned int count, FILE *fp);
函数功能:将buffer指向的内存中的数据块写入fp所指的文件,buffer是待输出数据块的起始地址,size是每个数据块的大小(待输出的每个数据块的字节数),count是最多允许写入的数据块个数(每个数据块size个字节),函数返回的是实际写入的数据块个数。
(tips:用户指定的内存块大小,最小为1字节,最大为整个文件)
fread()函数和fwrite()函数是按数据块的长度来处理输入/输出的,在用文本编辑器打开文本文件时可能因发生字符转换而出现莫名其妙的结果,所以这两个函数通常用于二进制文件的输入/输出
随机存取函数
fseek()函数原型:
_CRTIMP int __cdecl fseek(FILE *, long, int);
对流式文件可以进行顺序读写,也可以进行随机读写。关键在于控制文件的位置指针,如果位置指针是按字节位置顺序移动的,就是顺序读写。如果能将位置指针按需要移动到任意位置,就可以实现随机读写。所谓随机读写,是指读完上一个字符(字节)后,并不一定要读写其后续的字符(字节),而可以读写文件中任意位置上所需要的字符(字节)。
该函数的调用形式为:
int fseek(FILE *fp,long offset,int fromwhere);
函数功能:将fp的文件位置指针从fromwhere开始移动offset个字节指示下一个要读取的数据的位置
offset是一个偏移量,它告诉文件位置指针要跳过多少字节,offset为正时,向后移动,为负时,向前移动,ANSIC要求位移量offset是长整型数据(常量数据后要加L),
这样当文件的长度大于64k时不至于出问题
fromwhere用于确定偏移量计算的起始位置,它的可能取值有3种
SEEK_SET或0,代表文件开始处;
SEEK_CUR或1,代表文件当前位置;
SEEK_END或2,代表文件结尾处,
通过指定fromwork和offset的值,可使位置指针移动到文件的任意位置,从而实现文件的随机读取,
如果函数fseek()调用成功,则返回0值,否则返回非0值
两种特殊使用方式:
将读写位置移动到文件开头:fseek(FILE *stream,0,SEEK_SET);
将读写位置移动到文件尾:fseek(FILE *stream,0,0SEEK_END);
(fseek() 一般用于二进制文件,在文本文件中由于要进行转换,计算的位置有时会出错)
ftell()函数
函数原型:long ftell(FILE *fp);
函数功能:读取当前文件指针的位置,若函数调用成功,则返回文件的当前读写位置,否则返回-1L,
函数ftell()用于相对于文件起始位置的字节偏移量来表示返回的当前位置指针(需要注意的是,当用err = fopen_s(&fp, "E:\\ww.txt", "a+");打开文件后文件指针移到文件末尾,此时文件起始位置还是为文件头部并不是文件末尾)
feof()函数
函数原型:int feof(FILE *fp);
函数功能:检测流上的文件结束符,如果文件结束,则返回非0值,否则返回0,文件结束符只能被clearerr()函数清除
(函数feof()总是在读完文件所有内容后再执行一次读文件操作(将文件结束符读走,但不显示)才能返回真(非0)值)
C语言为了提高数据的输入/输出的速度,在缓冲型文件系统中,给打开的每一个文件建立一个缓冲区,文件内容先被批量地读入缓冲区,程序进行读操作时,实际上是从缓冲区中读取数据,
写入操作也是如此,首先将数据写入缓冲区,然后在适当的时候(例如关闭时)再批量写入磁盘,这样虽然可以提高I/O的性能,但也有一些副作用,例如在缓冲区内容还未写入磁盘时,计算机突然死机或掉电,数据就会丢失,永远也找不回来,再如缓冲区被写入无用的数据时,如果不清除,其后的文件读操作都首先要读取这些无用的数据
文件结束判定
被错误使用的feof
牢记:在文件读取过程中,不能用feof函数的返回值直接用来判断文件的是否结束。
而是应用于当文件读取结束的时候,判断是读取失败结束,还是遇到文件尾结束。
- 文本文件读取是否结束,判断返回值是否为EOF (fgetc),或者NULL(fgets) 例如:
fgetc判断是否为EOF.
fgets判断返回值是否为NULL. - 二进制文件的读取结束判断,判断返回值是否小于实际要读的个数。
例如: fread判断返回值是否小于实际要读的个数。
文件读写的出错检测
ferror 函数原型:
int ferror(FILE *stream);
作用:在调用各种输入输出函数(如 putc、getc、fread、fwrite等)时,如果出现错误,除了函数返回值有所反映外,还可以用ferror函数检查
返回值:返回0表示未出错,返回非0值表示出错
参数说明:
*stream:文件指针
clearerr函数原型:
void clearerr(FILE *stream);
作用:使文件错误标志和文件结束标志置为0。假设在调用一个输入输出函数时出现了错误,ferror函数值为一个非零值。在调用clearerr(fp)后,ferror(fp)的值变为0。只要出现错误标志,就一直保留,直到对同一文件调用clearerr函数或rewind函数,或任何其他一个输入输出函数。
参数说明:
*stream:文件指针
知识点习题
- 以下函数中,和其他函数不属于一类的是__。
A. fwrite
B. putc
C. pwrite
D. putchar
E. getline
F. scanf
正确答案
C
答案解析:
pwrite是系统调用,其他都是库函数
系统调用: 系统调用发生在内核态,面向底层文件访问。
库函数调用: 即通常所说的应用编程接口API ,发生在用户态,面向应用。
常见文件系统(系统函数)
- fcntl 文件控制
- open 打开文件
- creat 创建新文件
- close 关闭文件描述字
- read 读文件
- write 写文件
- readv 从文件读入数据到缓冲数组中
- writev 将缓冲数组里的数据写入文件
- pread 对文件随机读
- pwrite 对文件随机写
- fwrite :将数据写入文件流;
- putc :将一个字符写入文件流中;
- putchar:向终端输出一个字符;
- getline: 从输入流中读入一个字符串;
- scanf:格式输入函数;
- pwrite:系统调用,写入起始地址的 偏移量 ,写入地址=文件开始+ offset 。
- 设fp已定义,执行语句fp=fopen(“file”,“w”);后,以下针对文本文件file操作叙述的选项错误的是:
A. 可以随意读和写
B. 只能写不能读
C. 可以在原有内容后追加写
D. 写操作结束后可以从头开始读
正确答案: ACD
答案解析:
用“w”打开的文件只能向该文件写入。 若打开的文件不存在,则以指定的文件名建立该文件,若打开的文件已经存在,则将该文件删去,重建一个新文件