本文将介绍常用的关于文件操作函数,如fopen,fclose,fread,fwrite,feek,ftell,rewind以及feof和ferror等文件操作操作函数,还介绍一些用于所有输入输出流的函数如fgetc,fputc,fgets,fputs,fprintf,fscanf等函数,还介绍了sscanf,sprintf函数,feof和ferror函数。最后介绍了文件文件缓冲区的存在。
目录
1、文件的介绍
2、文件的打开和关闭
①流的介绍
②文件指针
③文件的打开和关闭
④顺序读写函数介绍
(1)fgetc函数介绍
(2) fputc函数介绍
(3)fgets函数介绍
(4)fputs函数介绍
(5)fscanf函数介绍
(6)fprintf函数介绍
(7)fwrite函数介绍
(8)fread函数介绍
3、scanf,printf,sscanf,sprintf,fscanf,fscanf函数的区别
(1)sscanf函数介绍
(2)sprintf函数介绍
4、文件的随机读写
(1)ftell函数介绍
(2)rewind
5、文件读取结束的判定
feof函数介绍
ferror函数介绍
6、文件缓冲区
1、文件的介绍
我们写的程序的数据是存储在电脑的内存中,当程序退出,内存回收,数据就会丢失,当再次想要看到上次程序的结果是看不到的,想要将数据进行持久化的保存(程序退出其数据不丢失),我们可以使用文件来对这些数据进行保存。
从文件功能方面来分类,文件有程序文件和数据文件。程序文件包括源程序文件(如后缀为.c文件),目标文件(Windows系统其后缀名为.obj),可执行文件(Windows系统下其后缀名为.exe)。数据文件,其文件的内容不一定是程序,而是程序运行时读写的数据,比如程序运行需要从中读取数据的文件或输出内容的文件。下面介绍的是数据文件。根据数据的组织形式,数据文件被称为二进制文件和文本文件,如果数据在内存中以二进制的形式存储(不经任何转换),直接输出到外存文件中,这就是二进制文件。如果在外存中以ASCII码的形式存储,在存储前需要进行转换,以ASCII码的形式存储的文件就是文本文件。数据存储到外存的这个文件即可以二进制的形式进行存储也可以ASCII码的形式进行存储。这个区分主要是看这个外存(硬盘)里的存储的是二进制还是ASCII码的形式。注意,计算机中的数据在内存中都是二进制进行存储的,是文本文件还是二进制文件是针对外存(也就是硬盘来说的)。
2、文件的打开和关闭
①流的介绍
程序的数据可能会输出到各种外部设备,也可能从外部设备获取数据,不同的外部设备的输入输出操作各不相同,为了方便程序员对各种设备进行方便的操作,所以就抽象出来了流的概念。C语言程序对文件,画图,键盘等数据的输入操作都是通过流操作的。一般情况下,要想向流里写数据或者从流中读取数据都是要打开流然后进行操作。
值得注意的是,从键盘输入数据和向屏幕里打印信息都没有打开流,这是由于C语言程序在启动的时候默认打开了3个流(stdin,stdout,stderr)。
stdin - 标准输⼊流,在⼤多数的环境中从键盘输⼊,scanf函数就是从标准输入流中读取数据。
stdout - 标准输出流,大多数的环境中输出⾄显⽰器界⾯,printf函数就是将信息输出到标准输出
流中。
stderr - 标准错误流,大多数环境中输出到显示器界面。
stdin、stdout、stderr三个流的类型是: FILE * ,通常称为文件指针
C语⾔中,就是通过 FILE* 的文件指针来维护流的各种操作的
②文件指针
缓冲⽂件系统中,关键的概念是“⽂件类型指针”,简称“⽂件指针”
每个被使⽤的⽂件都在内存中开辟了⼀个相应的⽂件信息区,⽤来存放⽂件的相关信息(如⽂件的名字,⽂件状态及⽂件当前的位置等)。这些信息是保存在⼀个结构体变量中的。该结构体类型是由系统声明的,取名为FILE
比如在VS2013编译环境提供的 stdio.h 头文件中有以下的⽂件类型申明:
struct _iobuf {
char *_ptr;
int _cnt;
char *_base;
int _flag;
int _file;
int _charbuf;
int _bufsiz;
char *_tmpfname;
};
typedef struct _iobuf FILE;
不同的C编译器的FILE类型包含的内容不完全相同,但是大同小异。每当打开⼀个文件的时候,系统会根据⽂件的情况⾃动创建⼀个FILE结构的变量,并填充其中的信息,使⽤者不必关心其细节。⼀般都是通过⼀个FILE的指针来维护这个FILE结构的变量,这样使用起来更加方便。
下面创建一个FILE*的指针变量
FILE* pf;//⽂件指针变量
定义pf是⼀个指向FILE类型数据的指针变量。可以使pf指向某个⽂件的⽂件信息区(是⼀个结构体变量)。通过该⽂件信息区中的信息就能够访问该⽂件。也就是说,通过⽂件指针变量能够间接找到与它关联的⽂件。
③文件的打开和关闭
文件在读写之前应该先打开文件,在使用结束之后应该关闭文件,打开文件的同时都会返回一个FILE*的指针变量指向该文件,这就相当于简历了指针和文件的关系。ANSIC规定使用fopen函数来打开文件,fclose来关闭文件。
//打开⽂件
FILE * fopen ( const char * filename, const char * mode );
//关闭⽂件
int fclose ( FILE * stream );
#include<stdio.h>
int main()
{
FILE* pf = fopen("data.txt", "w");
//以写的形式打开data.txt文件
if (pf == NULL)
{
perror("fopen");
return 1;
}
//进行文件操作
//关闭文件
fclose(pf);
pf = NULL;
return 0;
}
④顺序读写函数介绍
所有输⼊流⼀般指适⽤于标准输⼊流和其他输⼊流(如⽂件输⼊流);所有输出流⼀般指适⽤于标准输出流和其他输出流(如⽂件输出流)。
需要注意的是,对流进行操作的时候一定要与对应的函数就行操作,如对文件操作,对文件以读的形式打开那就只能读取文件的信息而不能向文件输出信息,同样地,如果以写的形式打开,那就只能对文件进行写操作而不能对其进行读操作,如果需要又读又写那就需要再打开文件的时候设定读写的模式。
(1)fgetc函数介绍
FILE*表示一个流,(如文件流或标准输入输出流),该函数读取流中的一个字符,当读到文件末尾或读取错误的时候会返回一个EOF。当需要将一个流的信息都读取出来可以使用这个函数一个字符一个字符的读取直至返回值为EOF(读取结束),下面将使用代码展示
代码示例:
#include<stdio.h>
int main()
{
FILE* pf = fopen("data.txt", "r");
//以读的形式打开data.txt文件
if (pf == NULL)
{
perror("fopen");
return 1;
}
//进行文件操作
char ch = 0;
while ((ch = fgetc(pf)) != EOF)//当fgetc返回值不为EOF就表示在没有读取失败或还没到文件末尾
{
printf("%c", ch);
}
//关闭文件
fclose(pf);
pf = NULL;
return 0;
}
其中data.txt文件(与C语言的源文件在同一个目录底下)的内容如下:
最后结果如下:
(2) fputc函数介绍
该函数的功能是向流中写一个字符 ,如果写入成功将返回该字符,写入失败就会返回EOF。
代码示例:
#include<stdio.h>
int main()
{
FILE* pf = fopen("data.txt", "w");
//以读的形式打开data.txt文件
if (pf == NULL)
{
perror("fopen");
return 1;
}
//进行文件操作,向data.txt文件写入一个字符x
fputc('x', pf);
//关闭文件
fclose(pf);
pf = NULL;
return 0;
}
(3)fgets函数介绍
该函数是向流中的数据读取num个字符将其放到str这个指针所指向的空间。如果读取成功就返回str(其实就是这个字符串的地址),如果读取失败或读到文件结尾就会返回NULL。当需要将一个流的信息都读取出来可以使用这个函数一次读取num个,直至返回值为NULL(读取结束),下面将使用代码展示
代码示例:
#include<stdio.h>
int main()
{
FILE* pf = fopen("data.txt", "r");
//以读的形式打开data.txt文件
if (pf == NULL)
{
perror("fopen");
return 1;
}
char ch[20] = { 0 };
while (fgets(ch, 5, pf)!=NULL)//将data.txt文件的内容每次读取5个放到ch数组中,直至读取错误或文件末尾
{
printf("%s", ch);//将读取的
}
//关闭文件
fclose(pf);
pf = NULL;
return 0;
}
(4)fputs函数介绍
该函数是将str指向的内容写到流中,如果写入成功就会返回应该非0的数,如果写入失败就会返回0。
代码示例:
#include<stdio.h>
int main()
{
FILE* pf = fopen("data.txt", "w");
//以读的形式打开data.txt文件
if (pf == NULL)
{
perror("fopen");
return 1;
}
char ch[30] = "asdfahyrhdfgqefd7890";
fputs(ch, pf);
//关闭文件
fclose(pf);
pf = NULL;
return 0;
}
(5)fscanf函数介绍
该函数的功能是从流中读取数据并按特定的格式存储到format这个参数里(这个参数与scanf类似)。如果在读取过程中发生读取错误或到达文件末尾,会设置相应的指示器(feof或ferror)。如果在任何数据成功读取之前发生其中之一,则返回EOF。
代码示例:
#include<stdio.h>
int main()
{
char ch[30] = { 0 };
FILE* pf = fopen("data.txt", "r");//以读的形式打开文件
fscanf(pf, "%s", ch);
printf("%s\n", ch);
return 0;
}
(6)fprintf函数介绍
该函数是将format所指向的字符内容输出到stream这个流中,如果发生写入错误,文件错误指示器(ferror)会被设置,同时函数通常会返回一个负数,用以表示错误的发生。如果成功的话就会返回写入流中字符的个数。
代码示例:
#include<stdio.h>
int main()
{
char ch[30] = "12345679zxcvasdfbgre";
FILE* pf = fopen("data.txt", "w");//以写的形式打开文件
fprintf(pf, "%s", ch);
printf("%s\n", ch);
return 0;
}
(7)fwrite函数介绍
该函数是将ptr所指向的size*count个字节的内容写入到流中,返回值成功写入的个数。该函数是对二进制文件进行操作的。
#include<stdio.h>
int main()
{
char ch[30] = "12345679zxcvasdfbgre";
FILE* pf = fopen("test", "wb");//以二进制读写的形式打开文件
fwrite(ch,sizeof(char), sizeof(ch), pf);
//char tmp[30] = { 0 };
//fread(tmp, sizeof(char), sizeof(tmp), pf);
//printf("%s\n", tmp);
return 0;
}
(8)fread函数介绍
该函数向流中取size*count个字节的数据放入到ptr所指向的空间。返回的是读取成功的个数。可以通过每次读取一个直至返回值为0就表示读取结束或读取失败。这可以通过ferror或feof来对其进行判断,看是正常结束还是不正常结束。需要注意的是该函数是对二进制文件进行读取的
代码示例:
#include<stdio.h>
int main()
{
//char ch[30] = "12345679zxcvasdfbgre";
FILE* pf = fopen("test", "rb");//以二进制读写的形式打开文件
//fwrite(ch,sizeof(char), sizeof(ch), pf);
char tmp[30] = { 0 };
fread(tmp, sizeof(char), sizeof(tmp), pf);
printf("%s\n", tmp);
return 0;
}
需要注意的是,这些函数如果对文件流进行操作时,打开文件的时候要留意是读文件还是写文件,可以从文件中读取的有fgetc,fgets,fscanf,fread函数。而将数据输出到文件中的函数有fputc,fputs,fprintf,fwrite函数。fwrite和fread只能针对文件流进行操作(还是以二进制进行操作的),而上面其余的函数是针对所以输入或输出流。
3、scanf,printf,sscanf,sprintf,fscanf,fscanf函数的区别
(1)sscanf函数介绍
该函数是在字符串读取格式化的数据。
代码示例:
#include<stdio.h>
int main()
{
char str[15] = "10 1.23 avb";
int a = 0;
float b = 0;
char ch[5] = { 0 };
sscanf(str, "%d%f%s", &a, &b, ch);
printf("%d\n", a);
printf("%f\n", b);
printf("%s\n", ch);
return 0;
}
(2)sprintf函数介绍
该函数是把格式化的数据转化成字符串
#include<stdio.h>
int main()
{
char str[30] = "10 1.23 avb";
int a = 0;
float b = 0;
char ch[5] = { 0 };
char tmp[30] = { 0 };
sscanf(str, "%d%f%s", &a, &b, ch);
sprintf(tmp, "%d %f %s", a, b, ch);
printf("%s", tmp);
return 0;
}
小结:scanf从标准输入流读取格式化数据,fscanf从指定的输入流上读取格式化的数据,sscanf在字符串中读取格式化的数据。printf把数据以格式化的形式打印在标准输出流上,fprintf把数据以格式化的形式输出到指定的流,sprintf把格式化的数据转化成字符串。
4、文件的随机读写
(1)fseek函数介绍
#include<stdio.h>
int main()
{
FILE*pf=fopen("tt.txt", "w");
if (pf == NULL)
{
perror("fopen");
return 1;
}
fputs("This is an apple.", pf);
fseek(pf, 9, SEEK_SET);//通过设置后两个参数,来设定光标的位置
fputs(" sam", pf);
char ch = 0;
while ((ch=fgetc(pf))!=EOF)
{
printf("%c", ch);
}
fclose(pf);
return 0;
}
(1)ftell函数介绍
该函数是返回文件指针相对于于起始位置的偏移量
#include<stdio.h>
int main()
{
FILE*pf=fopen("tt.txt", "w");
if (pf == NULL)
{
perror("fopen");
return 1;
}
fputs("This is an apple.", pf);
fseek(pf, 9, SEEK_SET);
fputs("xxx",pf);
int ret=ftell(pf);
printf("%ld\n", ret);//打印的是光标相对于起始的偏移量
return 0;
}
(3)rewind
让光标回到文件的起始位置。
#include<stdio.h>
int main()
{
FILE*pf=fopen("tt.txt", "w");
if (pf == NULL)
{
perror("fopen");
return 1;
}
fputs("This is an apple.", pf);
fseek(pf, 9, SEEK_SET);
fputs("xxx",pf);
int ret=ftell(pf);
printf("%ld\n", ret);//打印的是光标相对于起始的偏移量
rewind(pf);
ret = ftell(pf);
printf("%ld\n", ret);//打印的是光标相对于起始的偏移量
return 0;
}
5、文件读取结束的判定
feof函数介绍
在文件读取过程中,不能用feof函数的返回值直接来判断文件的是否结束。feof 的作用是:当文件读取结束的时候,判断是读取结束的原因是否是:遇到文件尾结束。如果是遇到文件末尾结束的话就会返回一个非0的数,如果不是文件末尾结束的话将返回0。
ferror函数介绍
如果设置了与流关联的错误指示符,则返回一个非零值。否则,返回0。
6、文件缓冲区
ANSIC标准采用“缓冲文件系统”处理的数据文件的,所谓缓冲文件系统是指系统自动地在内存中为
程序中每⼀个正在使用的文件开辟⼀块“文件缓冲区”。从内存向磁盘输出数据会先送到内存中的缓
冲区,装满缓冲区后才⼀起送到磁盘上。如果从磁盘向计算机读入数据,则从磁盘文件中读取数据输入到内存缓冲区(充满缓冲区),然后再从缓冲区逐个地将数据送到程序数据区(程序变量等)。缓冲区的大小是根据C编译系统决定的。
因为有缓冲区的存在,C语言在操作文件的时候,需要做刷新缓冲区或者在文件操作结束的时候关闭文件。如果不刷新缓冲区或关闭文件可能会导致数据丢失等问题。