文章目录

    • 1.为什么使用件?
    • 2. 什么是文件?
    • 3. 二进制文件和文本文件?
    • 4. 文件的打开和关闭
    • 5. 文件的顺序读写
    • 6. 文件的随机读写
    • 7. 文件读取结束的判定
    • 8. 文件缓冲区

1.为什么使用件?

如果没有文件,我们写的程序的数据是存储在电脑的内存中,如果程序退出,内存回收,数据就丢失了,等再次运行程序,是看不到上次程序的数据的,如果要将数据进行持久化的保存,我们可以使用文件。

2. 什么是文件?

磁盘上的文件是文件。
但是在程序设计中,我们⼀般谈的文件有两种:程序文件、数据文件(从文件功能的角度来分类的)。
(1)程序文件

程序文件包括源程序文件(后缀为.c),目标⽂件(windows环境后缀为.obj),可执行程序(windows环境后缀为.exe)。

(2)数据文件

文件的内容不⼀定是程序,而是程序运行时读写的数据,比如程序运行需要从中读取数据的文件,或者输出内容的文件。

我们接下来要讨论的就是数据文件

在以前我们所处理数据的输入输出都是以终端为对象的,即从终端的键盘输⼊数据,运行结果显示到显示器上。
其实有时候我们会把信息输出到磁盘上,当需要的时候再从磁盘上把数据读取到内存中使用,这里处理的就是磁盘上文件。

(3)文件名

⼀个文件要有⼀个唯⼀的文件标识,以便用户识别和引用。 文件名包含3部分:文件路径+文件名主干+文件后缀 例如:
c:\code\test.txt

3. 二进制文件和文本文件?

根据数据的组织形式,数据文件被称为文本文件或者二进制文件。
数据在内存中以二进制的形式存储,如果不加转换的输出到外存,就是二进制文件。
如果要求在外存上以ASCII码的形式存储,则需要在存储前转换。以ASCII字符的形式存储的文件就是文本文件。
⼀个数据在内存中是怎么存储的呢?
字符⼀律以ASCII形式存储,数值型数据既可以用ASCII形式存储,也可以使用二进制形式存储。

4. 文件的打开和关闭

(1)关于流和标准流
流:

我们程序的数据需要输出到各种外部设备,也需要从外部设备获取数据,不同的外部设备的输入输出操作各不相同,为了方便程序员对各种设备进行方便的操作,我们抽象出了流的概念,我们可以把流想象成流淌着字符的河。
C程序针对文件、画面、键盘等的数据输入输出操作都是通过流操作的。
⼀般情况下,我们要想向流里写数据,或者从流中读取数据,都是要打开流,然后操作。

标准流:

那为什么我们从键盘输入数据,向屏幕上输出数据,并没有打开流呢? 那是因为C语言程序在启动的时候,默认打开了3个流:
• stdin –
标准输⼊流,在⼤多数的环境中从键盘输⼊,scanf函数就是从标准输⼊流中读取数据。
• stdout –
标准输出流,大多数的环境中输出至显示器界面,printf函数就是将信息输出到标准输出流中。
• stderr –
标准错误流,⼤多数环境中输出到显示器界面。 这是默认打开了这三个流,我们使⽤scanf、printf等函数就可以直接进行输入输出操作的。
stdin、stdout、stderr 三个流的类型是: FILE* ,通常称为文件指针。 C语言中,就是通过 FILE*
的文件指针来维护流的各种操作的。

(2)文件指针

缓冲文件系统中,关键的概念是“文件类型指针”,简称“文件指针”。
每个被使用的文件都在内存中开辟了⼀个相应的文件信息区,用来存放文件的相关信息(如文件的名字,文件状态及文件当前的位置等)。这些信息是保存在⼀个结构体变量中的。该结构体类型是由系统声明的,取名FILE
我们可以通过文件指针来使用它们

文件指针的创建

FILE * pf

定义pf是⼀个指向FILE类型数据的指针变量。可以使pf指向某个文件的文件信息区(是⼀个结构体变量)。通过该⽂件信息区中的信息就能够访问该文件。也就是说,通过文件指针变量能够间接找到与它关联的文件

如:

(3)文件的打开和关闭
我们使用文件时应该要先打开文件,当然在程序结束时也要关闭文件
我们分别通过两个函数来打开和关闭文件
打开:使用fopen函数

//打开文件FIL* E pf=fopen ( const char * filename, const char * mode );

filename指的是文件名
mode指的是打开的方式
打开失败返回NULL
关闭:使用fclose函数

//关闭文件int fclose ( FILE * stream );

stream指的是文件指针pf

打开模式

注意:使用写的时候文件的内容会先被清空哦
使用:

#includeint main() {//打开FILE* pf = fopen("data.txt", "w");//判断是否成功打开if (pf == NULL) {perror("fopen");return 1;}//使用文件for(int i=0;i<26;i++)fputc('a'+i, pf);//关闭fclose(pf);pf = NULL;return 0;}

5. 文件的顺序读写

顺序函数介绍:

(1)fputc函数
格式:

int fputc ( int character, FILE * stream );

参数:character:要写入的字符的 int 提升。写入时,该值在内部转换为无符号字符
stream:文件指针
返回值:成功后,将返回写入的字符。
如果发生写入错误,则返回 EOF 并设置错误指示符 (ferror)。
作用:
将字符输出到文件里
使用

//fputcint main() {//打开一个文件FILE* pf = fopen("data.txt", "w");//data.txt--文件名w-写(写到文件中)//判断是否为空if (pf == NULL) {perror("fopen");return 1;}//使用for(int i=0;i<26;i++)//fputc('a'+i, pf);//将数据输出到文件里//关闭fclose(pf);pf = NULL;return 0;}

运行结果:
打开文件就可以看到成功输出到文件中了

(2)fgetc函数
格式:

int fgetc ( FILE * stream );

参数:
指向标识输入流的 FILE 对象的指针。
返回值:
成功后,将返回字符读取(提升为 int 值)。
返回类型为 int 以适应特殊值 EOF,该值表示失败:
如果位置指示器位于文件末尾,则该函数返回 EOF 并设置流的 eof 指示(feof)。
如果发生其他读取错误,该函数也会返回 EOF,但会设置其错误指示器 (ferror
作用:
将文件字符输入
使用:

int main() {char arr[27];//存储字符FILE* pf = fopen("data.txt", "r");//打开文件,r-读(从文件中读取)if (pf == NULL) {perror("fopen");return 1;}//将字符输入到字符数组中for (int i = 0; i < 26; i++)arr[i]=fgetc(pf);arr[26] = '\0';printf("%s\n", arr);fclose(pf);pf = NULL;return 0;}

运行结果:

(3)fputs函数
格式:

int fputs ( const char * str, FILE * stream );

参数:
str :包含要写入流的内容的 C 字符串
stream :指向标识输出流的 FILE 对象的指针。
返回值:
成功后,将返回一个非负值。
出错时,该函数返回 EOF 并设置错误指示器 (ferror)。
作用:
将一个字符串输出到文件中
使用:

int main() {FILE* pf = fopen("data.txt", "w");if (pf == NULL) {perror("fopen");return 1;}//使用fputs("abcder", pf);fclose(pf);pf = NULL;return 0;}

运行结果:
打开文件观察

(4)fgets函数
格式:

char * fgets ( char * str, int num, FILE * stream );

参数:
str:指向复制字符串读取的 char数组的指针
num:要复制到 str 中的最大字符数(包括终止 null 字符),真实输入的是num-1个字符
stream:文件指针
返回值:成功后,该函数返回 str。
如果在尝试读取字符时遇到文件末尾,则设置 eof 指示符 (feof)。如果在读取任何字符之前发生这种情况,则返回的指针为 null 指针(并且 str 的内容保持不变)。
如果发生读取错误,则设置错误指示符 (ferror) 并返回 null 指针(但 str 指向的内容可能已更改)。

作用:
将文件中的字符串输入
使用:

 //fgetsint main() {int arr[26];FILE* pf = fopen("data.txt", "r");if (pf == NULL) {perror("fopen");return 1;}//使用 fgets(arr,5,pf); printf("%s", arr);fclose(pf);pf = NULL;return 0;}

运行结果:

(5)fprintf函数
格式:

int fprintf ( FILE * stream, const char * format, ... );

参数:
stream:指向标识输出流的 FILE 对象的指针。
format:这是一个可变参数,和pringf类似
返回值:功后,将返回写入的字符总数。
如果发生写入错误,则设置错误指示符(ferror)并返回负数。
如果在写入宽字符时发生多字节字符编码错误,则将 errno 设置为 EILSEQ 并返回负数。
作用:将按格式指向的 C 字符串写入流。如果 format 包含格式说明符(以 % 开头的子序列),则格式化 format 后面的其他参数并将其插入到生成的字符串中,以替换其各自的说明符。
使用:

int main() {FILE* pf = fopen("data.txt", "w");if (pf == NULL) {perror("fopen");return 1;}//使用fprintf(pf, "%d %d %c", 21,55, 'c');fclose(pf);pf = NULL;return 0;}

运行结果:

(6)fscanf函数
格式:

int fscanf ( FILE * stream, const char * format, ... );

参数:与fprintf一样
返回值:
成功后,该函数返回已成功填充的参数列表的项数。此计数可以与预期的项目数匹配。
如果在读取时发生读取错误或达到文件末尾,则设置正确的指示器(feof 或 ferror)。而且,如果在成功读取任何数据之前发生任何一种情况,则返回 EOF。
作用:
从流中读取数据,并根据参数格式将其存储到其他参数所指向的位置。
其他参数应指向已分配的对象,该对象由格式字符串中的相应格式说明符指定。
使用:

int main() {FILE* pf = fopen("data.txt", "r");if (pf == NULL) {perror("fopen");return 1;}int a, b;charc;//使用fscanf(pf, "%d %d %c",&a,&b,&c);printf("%d %d %c", a, b, c);fclose(pf);pf = NULL;return 0;}

运行结果:

(7)fwrite函数
格式:

size_t fwrite ( const void * ptr, size_t size, size_t count, FILE * stream );

参数:
prt:指向要写入的元素数组的指针,转换为 const void*
size:要读取的每个元素的大小(以字节为单位)。size_t 是无符号整型。
count:元素的数量,每个元素的大小为 bytes。size_t 是无符号整数类型。
stream:文件指针
返回值:返回成功写入的元素总数。
如果此数字与 count 参数不同,则写入错误会阻止函数完成。在这种情况下,将为流设置错误指示器 (ferror)。
如果 size 或 count 为零,则函数返回零,错误指示器保持不变。
作用:
进行二进制文件的输出
使用:

//fwriteint main() {intp[3] = {1,4,5};FILE* pf = fopen("data.txt", "w");if (pf == NULL) {perror("fopen");return 1;}fwrite(p, 4, 3, pf);fclose(pf);pf = NULL;return 0;}

运行结果:

由于我们这个是文本文件所以无法看到内容

(8)fread函数
格式:

size_t fread ( void * ptr, size_t size, size_t count, FILE * stream );

参数:
ptr:指向大小至少为 (sizecount) 字节的内存块的指针,该块转换为 void
size:要读取的每个元素的大小(以字节为单位)。size_t 是无符号整型。
count:元素的数量,每个元素的大小为 bytes。size_t 是无符号整数类型。
stream:文件指针
返回值:
返回成功读取的元素总数。
如果此数字与 count 参数不同,则表示读取时发生读取错误或已达到文件末尾。在这两种情况下,都设置了正确的指示器,可以分别用 ferror 和 feof 进行检查。
如果 size 或 count 为零,则该函数返回零,并且流状态和 ptr 指向的内容保持不变。size_t 是无符号整数类型。
作用:
进行二进制文件的输入
使用:

//freadint main() {int arr[3] = { 0 };FILE* pf = fopen("data.txt", "r");if (pf == NULL) {perror("fopen");return 1;}//使用fread(arr, 3, 4, pf);printf("%d ", arr[0]);printf("%d ", arr[1]);printf("%d ", arr[2]);fclose(pf);pf = NULL;return 0;}

运行结果:

6. 文件的随机读写

(1)fseek函数
格式:

int fseek ( FILE * stream, long int offset, int origin );

参数:
stream:文件指针
offset:偏移多少
origin:从哪里开始偏移
返回值:
如果成功,该函数将返回零。
否则,它将返回非零值。
如果发生读写错误,则设置错误指示器 (ferror)。

作用:
对光标位置进行偏移
使用:

int main() {char arr[27];//存储字符FILE* pf = fopen("data.txt", "r");//打开文件,r-读(从文件中读取)if (pf == NULL) {perror("fopen");return 1;}int i;//将字符输入到字符数组中//使用,偏移到第二个位置fseek(pf, 2, SEEK_SET);for ( i = 0; i < 25; i++)arr[i]=fgetc(pf);arr[24] = '\0';printf("%s\n", arr);fclose(pf);pf = NULL;return 0;}

运行结果:
文件内容是abcdefghijklmnopqrstuvwxyz

(2)ftell函数
格式:

long int ftell ( FILE * stream );

参数:
一个文件指针
返回值:
返回偏移的位置(以文件开头为标准)
作用:
记录光标位置
使用:

int main() {char arr[27];//存储字符FILE* pf = fopen("data.txt", "r");//打开文件,r-读(从文件中读取)if (pf == NULL) {perror("fopen");return 1;}int i;fseek(pf, 2, SEEK_SET);for ( i = 0; i < 25; i++)arr[i]=fgetc(pf);arr[24] = '\0';//使用:int t=ftell(pf);printf("%d\n",t);fclose(pf);pf = NULL;return 0;}

运行结果:

(3)rewind
格式:

void rewind ( FILE * stream );

参数:文件指针
返回值为空
作用:
让文件指针的位置回到文件的起始位置

7. 文件读取结束的判定

(1)feof函数
格式:

int feof ( FILE * stream );

参数:文件指针
返回值:
如果设置了与流关联的文件结束指示符,则返回非零值。
否则,返回零。
作用:当文件读取结束的时候,判断是读取结束的原因是否是:遇到文件尾结束。
注意:在文件读取过程中,不能⽤feof函数的返回值直接来判断文件的是否结束
文本文件读取是否结束,判断返回值是否为 EOF ( fgetc ),或者 NULL ( fgets)
例如:
• fgetc 判断是否为 EOF .
• fgets 判断返回值是否为 NULL .
(2)ferror函数
格式:

int ferror ( FILE * stream );

参数:文件指针
返回值:
如果设置了与流关联的错误指示器,则返回非零值。
否则,返回零。
作用:检查是否出现错误

(3)关于上述函数的使用

#include #include int main(void){ int c; // 注意:int,⾮char,要求处理EOF FILE* fp = fopen("test.txt", "r"); if(!fp) { perror("File opening failed"); return EXIT_FAILURE; } //fgetc 当读取失败的时候或者遇到⽂件结束的时候,都会返回EOF while ((c = fgetc(fp)) != EOF) // 标准C I/O读取⽂件循环 {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;

8. 文件缓冲区

ANSIC 标准采用“缓冲文件系统”处理的数据文件的,所谓缓冲文件系统是指系统⾃动地在内存中为程序中每⼀个正在使用的文件开辟⼀块“文件缓冲区”。从内存向磁盘输出数据会先送到内存中的缓冲区,装满缓冲区后才⼀起送到磁盘上。如果从磁盘向计算机读入数据,则从磁盘文件中读取数据输⼊到内存缓冲区(充满缓冲区),然后再从缓冲区逐个地将数据送到程序数据区(程序变量等)。缓冲区的大小根据C编译系统决定的。

为什么会有缓冲区:

a.提高效率
b.因为有缓冲区的存在,C语言在操作文件的时候,需要做刷新缓冲区或者在文件操作结束的时候关闭文件。
如果不做,可能导致读写文件的问题

过程图:

以上就是我的分享了,如果有什么错误,欢迎在评论区留言。
最后,谢谢大家的观看!