目录
一、什么是文件
二、为什么使用文件
三、文件的命名
四、文件结构与文件类型指针
1.文件结构
2.文件类型指针
五、文件的打开和关闭
1.打开文件
2.关闭文件
六、文件的顺序读写
1.fgetc()
2.fputc()
3.fgets()
4.fputc()
5.fscanf()与fprintf()
6.fread()与fwrite()
对比两组函数:
七、文件随机读写
1.fseek()
2.ftell()
3.rewind()
八、其他函数
1.文件末尾检测函数feof()
2.读写错误检查函数ferror()
3.出错标记清除函数clearerr()
九、文本文件和二进制文件
十、文件缓冲区
(1)程序文件: 包括源程序文件(后缀为.c),目标文件(windows环境后缀为.obj),可执行程序(windows环境后缀为.exe)。
(2)数据文件:文件的内容不一定是程序,而是程序运行时读写的数据,比如程序运行需要从中读取数据的文件,或者输出内容的文件。数据文件还可分为各种类型,如:文本文件,图像文件,声音文件......
FILE是C语言为了具体实现对文件操作而定义的一个包含文件操作相关信息的结构类型。FILE是用typedef重命名的,在头文件
//FILE文件类型的说明
typedef struct
{
short level; //缓冲区使用量
unsigned flags; //文件状态标志
char fd; //文件描述符
short bsize; //缓冲区大小
unsigned char *buffer; //文件缓冲区的首地址
unsigned char *curp; //指向文件缓冲区的工作指针
unsigned char hold; //其他信息
unsigned istemp;
short token;
}FILE;
(1)文件缓冲区是内存中用于数据存储的数据块,在文件处理过程中,程序需要访问该缓冲区实现数据的存储。文件缓冲区由系统自动分配,并不想数组那样通过数组名如下标来定位。为此,C语言引进FILE文件结构,其成员指针指向文件缓冲区,通过移动指针实现对文件的操作。也就是说,通过文件指针变量能够找到与它关联的文件。
(2)FILE* fp; //FILE是文件类型定义符,fp是文件类型的指针变量
(3)文件指针不像以前普通指针那样进行能进行fp++或*fp等操作,fp++将意味着指向下一个FILE结构。文件操作具有顺序性的特点,前一个数据取出后,下一次将顺序取后一个数据,fp->curp会发生变化,但改变是隐含在文件读写操作中的,而不需要在编程时写上fp->curp++。
(1)打开文件功能用于建立系统与要操作的某个文件之间的关联,指定这个文件名并请求系统分配相应的文件缓冲区内存单元。打开文件由标准函数fopen()实现,其一般调用形式为:
FILE *fopen( const char *filename, const char *mode );
filename:文件名 mode:文件打开方式
该函数有返回值。如果执行成功,函数将返回包含文件缓冲区等信息的FILE结构地址,赋给文件指针fp。否则,返回一个NULL(空值)的FILE指针。
文件打开方式用来确定对打开的文件将进行什么操作。下表为C语言所有的文件打开方式。
文本文件 | 二进制文件 | ||||
使用方式 | 含义 | 如果指定文件不存在 | 使用方式 | 含义 | 如果指定文件不存在 |
"r" | 打开文本文件进行只读 | 出错 | "rb" | 打开二进制文件进行只读 | 出错 |
"w" | 建立新文本文件进行只写 |
建立一个新的文件
|
"wb" | 建立二进制文件进行只写 |
建立一个新的文件
|
"a" | 打开文本文件进行追加 |
建立一个新的文件
|
"ab" | 打开二进制文件进行追加 | 出错 |
"r+" | 打开文本文件进行读/写 | 出错 | "rb+" | 打开二进制文件进行读/写 | 出错 |
"w+" | 建立新文本文件进行读/写 |
建立一个新的文件
|
"wb+" | 建立二进制文件进行读/写 |
建立一个新的文件
|
"a+" | 打开文本文件进行读/写/追加 |
建立一个新的文件
|
"ab+" | 打开二进制文件进行读/写/追加 |
建立一个新的文件
|
执行标准函数fopen(),计算机完成下述步骤的工作:
①在磁盘中找到指定文件
②在内存中分配保存一个FILE类型结构的单元(16B)
③在内存中分配文件缓冲区单元(512B)
④返回FILE结构地址(会送给fp)
(2)文件打开的实质是把磁盘文件与文件缓冲区对应起来,这样后面的文件读写操作只需使用文件指针即可。为保证文件操作的可靠性,我们再调用fopen()时可以做一个判断,以保证文件正常打开后进行读写。如:
#include
int main()
{
FILE* fp = fopen("data.text", "r");
if (fp == NULL)
{
perror("fopen");
exit(0);
}
......
}
exit(0)是系统标准函数,作用是关闭所有打开的文件,并终止程序的执行。参数0表示程序正常结束,非0表示不正常的程序结束。
(3)C语言允许同时打开多个文件,不同文件采用不同文件指针指示,但不允许同一个文件在关闭前被再次打开
当文件操作完成后,应及时关闭它以防止不正常的操作。对于缓冲文件系统来说,文件的操作是通过缓冲区进行的。如果把数据写入文件,首先是写到文件缓冲区里,只有当写满512B,才会系统真正写入磁盘扇区。如果写的数据不到512B,发生程序异常终止,那么这些缓冲区中的数据将会被丢失。当文件操作结束时,即使未写满512B,通过文件关闭,能强制把缓冲区中的数据写入磁盘扇区,确保写文件的正常完成。除此之外,关闭文件还将释放文件缓冲区单元和FILE结构,使文件指针与具体文件脱钩。关闭文件通过调用标准函数fclose()实现。其一般格式为:
int fclose( FILE *stream );
stream:文件指针
该函数将返回一个整数,若该数为0表示正常关闭文件,否则表示无法正常关闭文件,所以关闭文件也应该使用条件判断,如:
if(fclose(fp))
{
printf("Can not close the file!\n");
exit(0);
}
功能 | 函数名 | 适用于 |
字符方式文件读入函数 | fgetc() |
所有输入流
|
字符方式文件写出函数 | fputc() |
所有输出流
|
字符串方式文件读入函数 | fgets() |
所有输入流
|
字符串方式文件写出函数 | fputs() |
所有输出流
|
格式化方式文件读 入函数
|
fscanf()
|
所有输入流
|
格式化方式文件写出函数 |
fprintf()
|
所有输出流
|
数据块方式文件读入函数 |
fread()
|
文件 |
数据块方式文件写出函数 |
fwrite()
|
文件 |
注意:
(1)读入:数据从文件---->内存
写出:数据从内存---->文件
(2) 流:一个高度抽象的概念,关注于如何写入各种外部设备,如:文件,屏幕,网络,光盘,软盘
c语言程序,就默认打开了三种流:标准输出流stdout,标准输入流stdin,标准错误流stderr,它们的类型都是FILE*
int fgetc( FILE *stream );
ch = fgetc(fp); //从fp所指示的磁盘文件读入一个字符到ch。函数返回值若读文件成功为ch,否则EOF(符号常量,其值为-1)。
int fputc( int c, FILE *stream );
fputc(ch,fp); //把一个字符ch写到fp所指示的磁盘文件上 。函数返回值若读文件成功为ch,否则EOF
在文件读写的过程中,fgetc()和fputc()实际上是对文件缓冲区进行读写,其工作过程与字符数组相似,存储单元由fp->curp指示。但文件操作很重要的一点是fp->curp会随fgetc()和fputc()的执行而自动改变,即
fputc(ch,fp); 等价于 (fp->curp) = ch; fp->curp++;
文件的特点之一就是文件数据长度可以不确定,只要外存空间足够,数据可以不受限制地写入文件中。
char *fgets( char *string, int n, FILE *stream );
string:可以是字符数组名或字符指针(指向字符串的指针)
n:指定读入的字符个数
stream:文件指针
该函数如果执行成功,返回读取的字符串,如果失败,则返回空指针,这时string的内容就不确定了
函数调用时,最多读取n-1个字符,并将读入的字符串存入指针string所指向内存地址开始的n-1个连续的内存单元中。当函数读取的字符达到指定的个数,或接收到换行符,或接收到文件结束标志EOF时,将在读取的字符后面自动添加一个‘\0’字符;若有换行符,则将换行符保留(换行符在'\0'之前);若有EOF,则不保留EOF。
int fputs( const char *string, FILE *stream );
string:要写入的字符串,可以是字符数组名、字符型指针变量或字符串常量
stream:文件指针
若函数执行成功,则返回所写的最后一个字符,否则,函数返回EOF
该函数把string写入文件时,字符串string的结束符'\0'不写入文件
(1)int fscanf( FILE *stream, const char *format [, argument ]... );
fscanf(文件指针,格式字符串,输入表)
如:FILE* fp; int n; fp=fopen("a.txt","r"); fscanf(fp,"%d",&n);//从文件a.txt读入整型数到变量n
(2)int fprintf( FILE *stream, const char *format [, argument ]...);
fprintf(文件指针,格式字符串,输出表)
如:FILE* fp; int n; fp=fopen("b.txt","w"); fprint(fp,"%d",n);//把变量n的数值写入文件b.txt
文件a.txt和文件b.txt是以文本方式打开的,但读写操作的数据并不是字符类型,变量n的数据在内存中是以二进制的形式存储的,两者间的不同由系统自动处理。
size_t fread( void *buffer, size_t size, size_t count, FILE *stream );//buffer:存放输入数据的首地址
size_t fwrite( const void *buffer, size_t size, size_t count, FILE *stream );//buffer:存放输出数据的首地址
void*:因为你不知道读写的数据是什么类型
size:数据块的字节数
count:要读写的数据块数
stream:文件指针
如:fread(da,4,5,fp);//从fp所指的文件中,每次读4个字节(一个实数)送入实数组fa中,连续读5次,即读5个实数到fa中。
注意:程序中用于输入的二进制文件无法用“记事本”等工具建立,它一般是其他程序或软件的处理结果。同样,作为程序结果的二进制文件也无法用“记事本”等工具查看
二进制文本文件的读写效率比文本文件要高,因为它不必把数据与字符做转换。另外,二进制文件更安全。
printf:打印格式化的数据输出到标准输出上(屏幕)
fprintf:把格式化的数据输出到所有输出流上
sprintf:把格式化的数据转化为一个字符串。int sprintf( char *buffer, const char *format [, argument] ... );
int fseek( FILE *stream, long offset, int origin );
stream:文件指针
offset:移动偏移量。它是long类型,使用常量时,应加上后缀L,oddset可以为负值,表示按相反方向计算偏移量,即为正时表示从当前向后计算,为负时从当前位置向前计算
origin:从哪个位置开始计算偏移量。位置可取3种值:文件首部、当前位置和文件尾部,实际表示时分别对应值0、1、2,或常量SEEK_SET、SEEK_CUR、SEEK_END
如:fseek(fp,20L,0);//表示将文件位置指针移动到离文件首20字节处
fseek(fp,-20L,SEEK_END);//表示将文件位置指针移动到离文件尾部前20字节处
long ftell( FILE *stream );
stream:文件指针(已经定义过的)
返回文件指针相对于起始位置的偏移量,此函数出错时,返回-1L。
void rewind( FILE *stream );
stream:文件指针
让文件指针的位置回到文件的起始位置
int feof( FILE *stream );
stream:文件指针
该函数成功返回1表示已经到了文件结束位置,0表示文件未结束。
注意:在文件读取过程中,不能用feof函数的返回值直接用来判断文件的是否结束。 而是应用于当文件读取结束的时候,判断是读取失败结束,还是遇到文件尾结束。
#include
#include
int main(void)
{
int c;
FILE* fp = fopen("test.txt", "r");
if(!fp)
{
perror("File opening failed");
return EXIT_FAILURE;
}
while ((c = fgetc(fp)) != EOF) // //fgetc 当读取失败的时候或者遇到文件结束的时候,都会返回EOF
{
putchar(c);
}
if (ferror(fp)) //判断是什么原因结束的
puts("I/O error when reading");
else if (feof(fp))
puts("End of file reached successfully");
fclose(fp);
return 0;
}
int ferror( FILE *stream );
stream:文件指针(必须是已经定义过的)
若返回值为0,表示未出错,否则表示出错
void clearerr( FILE *stream );
stream:文件指针(必须是已经定义过的)
该函数用来清除出错标志和文件结束标志,使它们为0
#include
#include
int main()
{
FILE* pf = fopen("C:\\Users\\86159\\Desktop\\doc.study.txt", "w");
fputs("abcdef", pf);//将代码放在缓冲区上
printf("睡眠10s,已经写入数据,打开doc.study.txt文件,发现文件没有内容\n");
Sleep(10000);
printf("刷新缓冲区\n");
fflush(pf);//刷新缓冲区,之后将缓冲区的数据写到文件中(磁盘);高版本vs上不能使用fflush
printf("再睡眠10s,再次打开doc.study.txt文件,发现文件有内容\n");
Sleep(10000);
fclose(pf);//fclose时也会刷新缓冲区
pf = NULL;
return 0;
}
由此可以得到结论:
因为有缓冲区的存在,C语言在操作文件的时候,需要做刷新缓冲区或者在文件操作结束的时候关闭文件。 如果不做,可能导致读写文件的问题。