思维导图:

 1. 为什么使用文件?

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

2. 什么是文件?

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

2.1 程序文件

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

2.2 数据文件

⽂件的内容不⼀定是程序,⽽是程序运⾏时读写的数据,⽐如程序运⾏需要从中读取数据的⽂件,或者输出内容的⽂件。
本章讨论的是数据⽂件。
在以前各章所处理数据的输⼊输出都是以终端为对象的,即从终端的键盘输⼊数据,运⾏结果显⽰到显⽰器上。
其实有时候我们会把信息输出到磁盘上,当需要的时候再从磁盘上把数据读取到内存中使⽤,这⾥处理的就是磁盘上⽂件。

2.3 文件名

⼀个⽂件要有⼀个唯⼀的⽂件标识,以便⽤⼾识别和引⽤。
⽂件名包含3部分:⽂件路径+⽂件名主⼲+⽂件后缀
例如: c:\code\test.txt
为了⽅便起⻅,⽂件标识常被称为⽂件名。

2.4 常见文件后缀

计算机中的文件后缀非常多,不同后缀对应不同的文件类型和用途。

一、程序文件(可执行或包含代码)

    •    .exe:Windows系统的可执行程序(如软件、游戏启动文件)。

    •    .msi:Windows的安装包程序(用于安装软件)。

    •    .bat / .cmd:Windows的批处理脚本(可自动执行一系列命令)。

    •    .sh:Linux/macOS的脚本文件(类似批处理,可执行命令)。

    •    .c / .cpp / .java / .py / .js:各种编程语言的源文件(包含代码,需编译/解释后执行)。

    •    .class:Java编译后的字节码文件(可被Java虚拟机执行)。

    •    .app:macOS的应用程序(本质是包含可执行文件的文件夹)。

    •    .apk:安卓系统的安装包(包含手机应用的可执行代码)。

二、数据文件(存储内容,需对应程序打开)

1. 文档类

    •    .txt:纯文本文件(记事本可打开)。

    •    .docx / .doc:Word文档(文字、格式)。

    •    .xlsx / .xls:Excel表格(数据、公式)。

    •    .pptx / .ppt:PowerPoint演示文稿(幻灯片)。

    •    .pdf:便携文档格式(跨平台的只读文档)。

2. 媒体类

    •    .jpg / .jpeg / .png / .gif:图片文件(分别对应不同压缩格式)。

    •    .mp3 / .wav / .flac:音频文件(音乐、声音)。

    •    .mp4 / .avi / .mov / .mkv:视频文件(电影、视频片段)。

3. 压缩/备份类

    •    .zip / .rar / .7z:压缩包(用于打包多个文件,节省空间)。

    •    .iso:光盘镜像文件(可模拟光盘内容,用于安装系统、游戏)。

4. 其他常用数据

    •    .html / .htm:网页文件(浏览器可打开)。

    •    .csv:逗号分隔的表格数据(Excel、记事本均可打开)。

    •    .json:存储结构化数据的文件(常用于程序间数据交换)。

核心区分小技巧:

    •    如果你双击一个文件,它能“自己启动并让电脑做事”(比如打开软件、运行游戏),大概率是程序文件(如.exe、.app)。

    •    如果你双击一个文件,必须先打开某个软件才能显示内容(比如用Word打开.docx,用播放器打开.mp4),那就是数据文件。

3. ⼆进制⽂件和⽂本⽂件

根据数据的组织形式,数据⽂件被称为⽂本⽂件和⼆进制⽂件。
数据在内存中以⼆进制的形式存储,如果不加转换的输出到外存的⽂件中,就是⼆进制⽂件。
如果要求在外存上以ASCII码的形式存储,则需要在存储前转换。以ASCII字符的形式存储的⽂件就是⽂本⽂件。
⼀个数据在⽂件中是怎么存储的呢?
字符⼀律以ASCII形式存储,数值型数据既可以⽤ASCII形式存储,也可以使⽤⼆进制形式存储。
如有整数10000,如果以ASCII码的形式输出到磁盘,则磁盘中占⽤5个字节(每个字符⼀个字节),⽽⼆进制形式输出,则在磁盘上只占4个字节
#include <stdio.h>
int main()
{int a = 10000;FILE* pf = fopen("test.txt", "wb");fwrite(&a, 4, 1, pf);//⼆进制的形式写到⽂件中fclose(pf);pf = NULL;return 0;}

ps:二进制文件本身的内容就不是文字,用文本工具打开自然会是乱码

ps:就是说二进制的程序文件可以在源文件下用二进制编辑器的打开方式去实现。

3.1  这边用二进制编译器输出时为啥没有ox?

ox 并不是二进制或十六进制数据本身的一部分,它只是人类为了区分进制而加上的 '标记'。

4. 文件的打开和关闭

4.1 流和标准流

4.1.1 流
我们程序的数据需要输出到各种外部设备,也需要从外部设备获取数据,不同的外部设备的输⼊输出操作各不相同,为了⽅便程序员对各种设备进⾏⽅便的操作,我们抽象出了流的概念,我们可以把流想象成流淌着字符的河。
C程序针对⽂件、画⾯、键盘等的数据输⼊输出操作都是通过流操作的。
⼀般情况下,我们要想向流⾥写数据,或者从流中读取数据,都是要打开流,然后操作。
4.1.2 标准流
那为什么我们从键盘输⼊数据,向屏幕上输出数据,并没有打开流呢?
那是因为C语⾔程序在启动的时候,默认打开了3个流:
  1. stdin - 标准输⼊流,在⼤多数的环境中从键盘输⼊,scanf函数就是从标准输⼊流中读取数据。
  2. stdout - 标准输出流,⼤多数的环境中输出⾄显⽰器界⾯,printf函数就是将信息输出到标准输出流中。
  3. stderr - 标准错误流,⼤多数环境中输出到显⽰器界⾯。
这是默认打开了这三个流,我们使⽤scanf、printf等函数就可以直接进⾏输⼊输出操作的。
stdin、stdout、stderr 三个流的类型是: FILE * ,通常称为⽂件指针。
C语⾔中,就是通过 FILE* 的⽂件指针来维护流的各种操作的。
4.1.3  为什么和 FILE*(文件指针)有关?

在C语言里,所有“流”(包括标准流、你自己打开的文件)都用 FILE* 类型的指针来表示,这个指针就像“管道的阀门”——通过它操作流里的数据。

    •    标准流对应的 FILE* 指针是系统预先定义好的:stdin(标准输入)、stdout(标准输出)、stderr(标准错误)。

    •    你自己用 fopen 打开的文件(比如 fopen("a.txt", "r")),得到的也是 FILE* 指针,本质上和标准流的指针是同一类东西,只是指向的“管道”不同(一个是默认的屏幕/键盘,一个是你指定的文件)。

4.2 件指针

缓冲⽂件系统中,关键的概念是“⽂件类型指针”,简称“⽂件指针”。
每个被使⽤的⽂件都在内存中开辟了⼀个相应的⽂件信息区,⽤来存放⽂件的相关信息(如⽂件的名字,⽂件状态及⽂件当前的位置等)。这些信息是保存在⼀个结构体变量中的。该结构体类型是由系统声明的,取名 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指向某个⽂件的⽂件信息区(是⼀个结构体变量)。通过该⽂件信息区中的信息就能够访问该⽂件。也就是说,通过⽂件指针变量能够间接找到与它关联的⽂件。
4.2.1 文件信息区

1. FILE结构体(你说的“文件信息区”)是程序和实际文件之间的“中间人”,程序通过它来操作文件内容。

打个比方:
你想往一个笔记本(实际文件)里写东西,不会直接拿笔戳笔记本——得先翻开笔记本(打开文件),这时FILE结构体就像笔记本的“封面+书签”:

    •    封面记录着笔记本的位置(存在硬盘哪里)、能不能涂改(读写权限);

    •    书签标记着当前写到哪一行(文件当前位置)。

2. FILE结构体和“程序打开的某个文件”是一一绑定的,但不是和“程序本身”绑定。

举个例子:
你写了一个程序,同时打开了a.txt和b.txt两个文件。这时程序里会有两个FILE结构体指针:

    •    一个指向 a.txt 的FILE结构体(记录a.txt的读写位置、权限等);

    •    另一个指向 b.txt 的FILE结构体(记录b.txt的信息)。

这两个结构体分别和 a.txt、b.txt绑定,程序通过它们分别操作两个文件。

  • 当你关闭a.txt后,对应的FILE结构体就会被释放,不再和任何文件绑定;
  • 而b.txt的结构体还在,直到你关闭它。
4.2.2 为啥说我们不需要特别关注到文件信息区的细节,而是通过它的指针形式找到它?

因为FILE结构体的内部细节对我们写程序来说,属于“没必要知道的底层实现”,就像你用手机拍照时,不用知道摄像头传感器的具体工作原理,按快门就行。

1. FILE结构体的内部太复杂,且不统一

它里面可能包含几十种信息(比如不同操作系统的FILE结构不一样),但我们编程时只需要“打开/读写/关闭”这几个动作。
就像你用遥控器开空调,不需要知道遥控器内部的电路板怎么设计——你只需要按“开”键,空调就工作了。
FILE指针就是那个“遥控器”,通过它调用fopen、fprintf等函数,就能完成操作,不用关心里面具体存了啥。

2. 用指针更安全、更方便

如果直接操作FILE结构体的内部数据(比如手动改它的“当前位置”),很容易改错(比如改到负数位置),导致程序崩溃。
而C语言提供的函数(如fread、fseek)已经帮我们封装好了这些操作,通过指针调用它们,相当于让“专业工具”来处理,不容易出错。

4.3 件的打开和关闭

⽂件在读写之前应该先打开⽂件,在使⽤结束之后应该关闭⽂件。
在编写程序的时候,在打开⽂件的同时,都会返回⼀个FILE*的指针变量指向该⽂件,也相当于建⽴了指针和⽂件的关系。
ANSI C 规定使⽤ fopen 函数来打开⽂件, fclose 来关闭⽂件。
 //打开⽂件FILE * fopen ( const char * filename, const char * mode );//关闭⽂件int fclose ( FILE * stream );
ps:这边的文件路径可以是windows上的任意文件,但是有两个限制:
  1. 权限问题
  2. 路径要对
mode表⽰⽂件的打开模式,下⾯都是⽂件的打开模式:
文件使用方式含义如果指定文件不存在
“r”(只读)为了输入数据,打开一个已经存在的文本文件出错
“w”(只写)为了输出数据,打开一个文本文件建立一个新的文件
“a”(追加)向文本文件尾添加数据建立一个新的文件
“rb”(只读)为了输入数据,打开一个二进制文件出错
“wb”(只写)为了输出数据,打开一个二进制文件建立一个新的文件
“ab”(追加)向一个二进制文件尾添加数据建立一个新的文件
“r+”(读写)为了读和写,打开一个文本文件出错
“w+”(读写)为了读和写,建议一个新的文件建立一个新的文件
“a+”(读写)打开一个文件,在文件尾进行读写建立一个新的文件
“rb+”(读写)为了读和写打开一个二进制文件出错
“wb+”(读写)为了读和写,新建一个新的二进制文件建立一个新的文件
“ab+”(读写)打开一个二进制文件,在文件尾进行读和写建立一个新的文件
实例代码:
/* fopen fclose example */
#include <stdio.h>
int main ()
{FILE * pFile;//打开⽂件pFile = fopen ("myfile.txt","w");//⽂件操作if (pFile!=NULL){fputs ("fopen example",pFile);//关闭⽂件fclose (pFile);}return 0;
}
4.3.1 文件路径中起始点的解析

假设程序当前运行的目录是 C:\A\B\C(即当前路径在 C 文件夹里),来看两个例子的区别:

例子1:.\\..\\..\\test.txt(1个 . + 2个 ..)

    •    解析步骤:

    1.    . → 从当前目录 C:\A\B\C 开始

    2.    第一个 .. → 跳至上一级 C:\A\B

    3.    第二个 .. → 再跳至上一级 C:\A

    4.    最终找 test.txt → 实际路径是 C:\A\test.txt

例子2:..\\..\\..\\test.txt(3个 ..)

    •    解析步骤:

    1.    从当前目录 C:\A\B\C 开始(默认起点,无需 .)

    2.    第一个 .. → 跳至 C:\A\B

    3.    第二个 .. → 跳至 C:\A

    4.    第三个 .. → 再跳至 C:\(A 文件夹的上一级是根目录 C:\)

    5.    最终找 test.txt → 实际路径是 C:\test.txt

核心区别:

    •    例子1的路径最终定位到 C:\A\test.txt(向上跳2级)

    •    例子2的路径最终定位到 C:\test.txt(向上跳3级)

跳转的层级不同,找到的文件位置自然不一样~

4.3.2  文件关闭和free有点相似

你混淆的点其实和「动态内存管理」「资源释放」的共性与差异有关,这几个概念确实容易串在一起,咱们分清楚核心逻辑就好了:

一、fclose() + 置空指针 与 free() + 置空指针的「异曲同工」之处

两者本质都是 “释放资源后避免误用”,逻辑完全一致:

    •    free(p):释放动态分配的内存(比如malloc申请的),但指针p仍保留原地址(变成野指针)。如果之后再用p读写,会访问无效内存,导致错误。

    •    fclose(fp):释放文件相关的系统资源(文件描述符、缓冲区等),但指针fp仍保留原地址(变成野指针)。如果之后再用fp操作文件,会访问已回收的资源,导致错误。

所以两者都需要在释放后 手动置空指针(p = NULL; 或 fp = NULL;),目的是把“隐藏的错误”变成“明确的空指针错误”,方便调试。

二、为什么有人会误认为“不置空会让程序一直运行”?

可能和 “内存泄漏”的概念混淆了。动态内存管理中:

    •    如果用malloc申请了内存,却忘记free,会导致 内存泄漏(系统内存被持续占用,无法回收)。

    •    对于长期运行的程序(比如服务器),内存泄漏累积到一定程度,可能导致系统内存不足,甚至程序崩溃(但不是“程序一直运行停不下来”,而是“运行中出问题”)。

但注意:

    •    内存泄漏的核心是“资源没释放”,和指针是否置空无关(哪怕free后没置空,资源也已经释放了,不会泄漏)。

    •    程序是否“停不下来”,只看代码逻辑(比如while(1)死循环),和内存/文件资源是否释放完全无关。

5. 件的顺序读写

5.1 顺序读写函数介绍

函数名功能适用于
fgetc字符输入函数所有输入流
fputc字符输出函数所有输出流
fgets文本行输入函数所有输入流
fputs文本行输出函数所有输出流
fscanf格式化输入函数所有输入流
fprintf格式化输出函数所有输出流
fread二进制输入文件输入流
fwrite二进制输出文件输出流
上⾯说的适⽤于所有输⼊流⼀般指适⽤于标准输⼊流和其他输⼊流(如⽂件输⼊流);所有输出流⼀般指适⽤于标准输出流和其他输出流(如⽂件输出流)。

5.2 对比⼀组函数:

scanf / fscanf / sscanf
printf / fprintf /  sprintf
5.2.1 scanf/fscanf/sscanf 的区别
  • scanf

功能:从标准输入流(通常是键盘)读取格式化数据。

原型int scanf(const char *format, ...);

示例

#include <stdio.h>int main() 
{int num;printf("请输入一个整数: ");scanf("%d", &num);printf("你输入的整数是: %d\n", num);return 0;
}

说明:程序运行时,会等待用户通过键盘输入数据,scanf 按照指定的格式 %d 读取整数并存储到变量 num 中。

  • fscanf

功能:从指定的文件流中读取格式化数据。

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

示例

#include <stdio.h>int main() 
{FILE *fp;int num;fp = fopen("test.txt", "r");if (fp != NULL) {fscanf(fp, "%d", &num);printf("从文件中读取的整数是: %d\n", num);fclose(fp);}return 0;
}
  • 说明:假设 test.txt 文件中存储了一个整数,fscanf 从打开的文件流 fp 中,按照 %d 的格式读取整数。它适用于从文件中读取特定格式的数据,而不是从标准输入读取。

  • sscanf

功能:从字符串中读取格式化数据。

原型int sscanf(const char *str, const char *format, ...);

示例

#include <stdio.h>int main() 
{char str[] = "123";int num;sscanf(str, "%d", &num);printf("从字符串中读取的整数是: %d\n", num);return 0;
}
  • 说明sscanf 从给定的字符串 str 中,按照 %d 的格式解析出整数,并存储到变量 num 中。它常用于从已有的字符串中提取特定格式的数据。
5.2.2  printf/fprintf/sprintf 的区别
  • printf

功能:将格式化的数据输出到标准输出流(通常是屏幕)。

原型int printf(const char *format, ...);

示例

#include <stdio.h>int main()
{int num = 10;printf("整数的值是: %d\n", num);return 0;
}
  • 说明printf 按照指定的格式 %d 将变量 num 的值输出到屏幕上。

  • fprintf

功能:将格式化的数据输出到指定的文件流中。

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

示例

#include <stdio.h>int main() 
{FILE *fp;int num = 20;fp = fopen("output.txt", "w");if (fp != NULL) {fprintf(fp, "整数的值是: %d\n", num);fclose(fp);}return 0;
}
  • 说明fprintf 将格式化的数据输出到打开的文件流 fp 指向的文件中,这里是将包含整数 num 值的字符串写入到 output.txt 文件。

  • sprintf

功能:将格式化的数据输出到字符串中。

原型int sprintf(char *str, const char *format, ...);

示例

#include <stdio.h>int main() 
{char result[50];int num = 30;sprintf(result, "整数的值是: %d\n", num);printf("生成的字符串是: %s", result);return 0;
}

说明sprintf 按照指定的格式将数据写入到字符数组 result 中,而不是直接输出到屏幕或文件,之后可以对这个字符串进行进一步的处理,比如传递给其他函数 。

5.3 我对内存和文件的总结

假设写了一个简单的程序:

#include <stdio.h>
int main() 
{FILE *f = fopen("test.txt", "r"); // 用r模式打开文件int num_from_file;fscanf(f, "%d", &num_from_file); // 从文件读数据(比如读到123)fclose(f); // 关闭文件int num_from_keyboard;printf("请输入一个数字:");scanf("%d", &num_from_keyboard); // 从键盘读数据(比如输入456)int result = num_from_file + num_from_keyboard; // 计算123+456printf("结果是:%d", result); // 屏幕显示589return 0;
}

这里的关键:

    •    test.txt是外部文件,里面的123是“长期存储”的(关了电脑也在)。

    •    程序运行时,会把123从文件读到num_from_file这个变量里——这个变量存在内存里(临时存储,程序结束就消失了)。

    •    你从键盘输入的456,会存在num_from_keyboard这个变量里——也在内存里,和文件没关系。

总结来了:

  1.  r模式的核心:只允许“读取文件内容”,绝对不能改文件本身(文件里的123永远是123)。
  2.  程序的临时变量:键盘输入的数字、计算过程中的数据,都存在程序的内存里(像局部变量这种),是程序自己的“临时工作区”,和被打开的文件没关系。   
  3. 文件和程序是分开的:用r模式打开文件,只是“借用”文件的内容来用,程序后续的代码(比如输入、计算、显示)都是服务于程序本身,不会碰文件一根毫毛。

简单说:r模式下,文件是“只读的素材”,程序是“处理素材的工具”,工具自己怎么折腾(临时变量、输入)都不影响素材本身。

6. 文件的随机读写

6.1 fseek

根据⽂件指针的位置和偏移量来定位⽂件指针(⽂件内容的光标)。
int fseek ( FILE * stream, long int offset, int origin );

例子:

/* fseek example */
#include <stdio.h>
int main ()
{FILE * pFile;pFile = fopen ( "example.txt" , "wb" );fputs ( "This is an apple." , pFile );fseek ( pFile , 9 , SEEK_SET );fputs ( " sam" , pFile );fclose ( pFile );return 0;
}

例子2:

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

参数说明

  • stream:文件指针(如 FILE *fp
  • offset:偏移量(字节数),可正可负(正数向后移,负数向前移)
  • origin:起始点(从哪里开始计算偏移),有 3 种取值:
    • SEEK_SET:从文件开头开始(值为 0)
    • SEEK_CUR:从当前位置开始(值为 1)
    • SEEK_END:从文件末尾开始(值为 2)
// 假设文件已打开(fp)
fseek(fp, 100, SEEK_SET);  // 移动到文件第100个字节处(从开头数)
fseek(fp, 50, SEEK_CUR);   // 从当前位置再向后移动50字节
fseek(fp, -30, SEEK_END);  // 从文件末尾向前移动30字节(定位到倒数第30字节)

6.2 ftell

返回⽂件指针相对于起始位置的偏移量
long int ftell ( FILE * stream );
例⼦:
/* ftell example : getting size of a file */
#include <stdio.h>
int main ()
{FILE * pFile;long size;pFile = fopen ("myfile.txt","rb");if (pFile==NULL)perror ("Error opening file");else{fseek (pFile, 0, SEEK_END); // non-portablesize=ftell (pFile);fclose (pFile);printf ("Size of myfile.txt: %ld bytes.\n",size);}
return 0;
}
  • 记录当前位置,方便后续回到该位置
  • 计算文件大小(先定位到文件末尾,再用 ftell 获取偏移量):
fseek(fp, 0, SEEK_END);  // 移动到文件末尾
long file_size = ftell(fp);  // 此时的偏移量就是文件总字节数

6.3 rewind

让⽂件指针的位置回到⽂件的起始位置(简化版的fseek)直接把文件指针移回文件开头,等价于fseek(fp, 0, SEEK_SET)
void rewind ( FILE * stream );
例⼦:
/* rewind example */
#include <stdio.h>
int main ()
{int n;FILE * pFile;char buffer [27];pFile = fopen ("myfile.txt","w+");for ( n='A' ; n<='Z' ; n++)fputc ( n, pFile);rewind (pFile);fread (buffer,1,26,pFile);fclose (pFile);buffer[26]='\0';printf(buffer);return 0;
}

区别:rewind 没有返回值,而 fseek 会返回 0(成功)或非 0(失败),可以判断操作是否成功。

6.4  三者总结

  • fseek 是 "主动移动" 指针到任意位置
  • ftell 是 "查询" 当前指针的位置
  • rewind 是 "快速复位" 指针到开头

7. 文件读取结束的判定

7.1 被错误使用的 feof

牢记:在⽂件读取过程中,不能⽤ feof 函数的返回值直接来判断⽂件的是否结束。
feof 的作⽤是:当⽂件读取结束的时候,判断读取结束的原因是否是:遇到⽂件尾结束。
1. ⽂本⽂件读取是否结束,判断返回值是否为 EOF ( fgetc ),或者 NULL ( fgets )
例如:
  • fgetc 判断是否为 EOF .
  • fgets 判断返回值是否为 NULL .
2. ⼆进制⽂件的读取结束判断,判断返回值是否⼩于实际要读的个数。
例如:
  • fread判断返回值是否⼩于实际要读的个数。
⽂本⽂件的例⼦:
#include <stdio.h>
#include <stdlib.h>int main(void)
{int c; // 注意:int,⾮char,要求处理EOFFILE* fp = fopen("test.txt", "r");if(!fp) {perror("File opening failed");return EXIT_FAILURE;}//fgetc 当读取失败的时候或者遇到⽂件结束的时候,都会返回EOFwhile ((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);
}
⼆进制⽂件的例⼦:
#include <stdio.h>enum { SIZE = 5 };
int main(void)
{double a[SIZE] = {1.,2.,3.,4.,5.};FILE *fp = fopen("test.bin", "wb"); // 必须⽤⼆进制模式fwrite(a, sizeof *a, SIZE, fp); // 写 double 的数组fclose(fp);double b[SIZE];fp = fopen("test.bin","rb");size_t ret_code = fread(b, sizeof *b, SIZE, fp); // 读 double 的数组if(ret_code == SIZE) {puts("Array read successfully, contents: ");for(int n = 0; n < SIZE; ++n)printf("%f ", b[n]);putchar('\n');} else  { // error handlingif (feof(fp))printf("Error reading test.bin: unexpected end of file\n");else if (ferror(fp)) {perror("Error reading test.bin");}}fclose(fp);
}

8. 文件缓冲区

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

8.1 考点一:缓冲区概念

通过一段简单的文件写入代码,展示数据先写入缓冲区,再写入磁盘的过程,让考生直观感受缓冲区的存在。
#include <stdio.h>
#include <stdlib.h>int main() 
{FILE *fp;fp = fopen("test.txt", "w");if (fp == NULL) {perror("fopen");exit(EXIT_FAILURE);}// 向文件写入数据,此时数据先写入缓冲区fputs("Hello, File Buffer!", fp); // 程序结束前,缓冲区数据会自动写入磁盘fclose(fp); return 0;
}
这⾥可以得出⼀个结论:
因为有缓冲区的存在,C语⾔在操作⽂件的时候,需要做刷新缓冲区或者在⽂件操作结束的时候关闭⽂件。
如果不做,可能导致读写⽂件的问题。
ps:说白了其实缓冲区是内存里的一块区域,用来临时存输入或输出的数据,能解决设备与 CPU 速度不匹配问题,提高运行效率 。
 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/pingmian/90776.shtml
繁体地址,请注明出处:http://hk.pswp.cn/pingmian/90776.shtml
英文地址,请注明出处:http://en.pswp.cn/pingmian/90776.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

SQL中的占位符、@Param注解和方法参数

代码中出现的多个 username 和 password 代表不同层面的变量&#xff0c;具体含义如下&#xff08;按执行顺序&#xff09;&#xff1a;### 1. Param("username") String username - 位置 &#xff1a;方法参数前的注解 - 作用 &#xff1a;- Param("username&q…

【SpringAI实战】FunctionCalling实现企业级自定义智能客服

一、前言 二、实现效果 三、代码实现 3.1 后端实现 3.2 前端实现 一、前言 Spring AI详解&#xff1a;【Spring AI详解】开启Java生态的智能应用开发新时代(附不同功能的Spring AI实战项目)-CSDN博客 二、实现效果 一个24小时在线的AI智能客服&#xff0c;可以给用户提供培…

kotlin基础【2】

变量类型var 和 val 的核心区别&#xff1a;关键字含义能否重新赋值类似概念&#xff08;Java&#xff09;varvariable&#xff08;可变变量&#xff09;可以普通变量&#xff08;无 final&#xff09;valvalue&#xff08;不可变变量&#xff09;不可以被 final 修饰的变量var…

【Spring AI】阿里云DashScope灵积模型

DashScope&#xff08;灵积模型&#xff09;是阿里云提供的大模型服务平台&#xff0c;集成了阿里自研的 通义千问&#xff08;Qwen&#xff09;系列大语言模型&#xff08;LLM&#xff09;以及多模态模型&#xff0c;为企业与开发者提供开箱即用的 AI 能力。官网地址 https://…

Rust Web框架性能对比与实战指南

Rust Actix Web Rust Web 框架的实用对比分析 以下是 Rust Web 框架的实用对比分析,涵盖主要框架(如 Actix-web、Rocket、Warp、Axum 等)的常见使用场景示例,按功能分类整理: 基础路由设置 Actix-web use actix_web::{get, App, HttpResponse, HttpServer, Responder}…

【解决vmware ubuntu不小心删boot分区,进不去系统】

如果仍然提示 Unable to locate package testdisk&#xff0c;有可能是源中不包含该工具&#xff08;LiveCD 使用的是“最小环境”&#xff09;。 &#x1fa9b; 解决方法&#xff1a;切换到国内完整软件源&#xff08;推荐&#xff09; 编辑 sources.list&#xff1a; sudo na…

04-netty基础-Reactor三种模型

1 基本概念Reactor模型是一种事件驱动&#xff08;Event-Driven&#xff09;的设计模式&#xff0c;主要用于高效处理高并发、I/O密集型场景&#xff08;如网络、服务器、分布式等&#xff09;。其核心思想就是集中管理事件&#xff0c;将I/O操作与业务逻辑解耦&#xff0c;避免…

踩坑无数!NFS服务从入门到放弃再到真香的血泪史

前言 说起NFS&#xff0c;我估计很多搞运维的兄弟都有一肚子话要说。这玩意儿吧&#xff0c;看起来简单&#xff0c;用起来坑多&#xff0c;但是真正搞明白了又觉得挺香的。 前几天有个朋友问我&#xff0c;说他们公司要搭建一个文件共享系统&#xff0c;问我推荐什么方案。我…

矩阵谱分解的证明及计算示例

1. 矩阵谱分解的条件矩阵的谱分解&#xff08;也称为特征分解&#xff09;是将一个矩阵分解为一系列由其特征向量和特征值构成的矩阵乘积的过程。进行谱分解的前提条件包括&#xff1a;<1.> 矩阵是可对角化的&#xff08;Diagonalizable&#xff09;&#xff0c;即矩阵存…

Leetcode 07 java

169. 多数元素 给定一个大小为 n 的数组 nums &#xff0c;返回其中的多数元素。 多数元素是指在数组中出现次数 大于 ⌊ n/2 ⌋ 的元素。 你可以假设数组是非空的&#xff0c;并且给定的数组总是存在多数元素。 示例 1&#xff1a; 输入&#xff1a;nums [3,2,3] 输出&a…

CS231n-2017 Lecture6训练神经网络(一)笔记

本节主要讲的是模型训练时的算法设计数据预处理&#xff1a;关于数据预处理&#xff0c;我们有常用的3个符号&#xff0c;数据矩阵X&#xff0c;假设其尺寸是&#xff0c;N是数据样本的数量&#xff0c;D是数据的维度均值减法(Mean subtraction)&#xff1a;是预处理最常用的形…

C++ 中实现 `Task::WhenAll` 和 `Task::WhenAny` 的两种方案

&#x1f4da; C 中实现 Task::WhenAll 和 Task::WhenAny 的两种方案 引用&#xff1a; 拈朵微笑的花 想一番人世變換 到頭來輸贏又何妨日與夜互消長 富與貴難久長 今早的容顏老於昨晚C 标准库异步编程示例&#xff08;一&#xff09;C TAP&#xff08;基于任务的异步编程…

【学习】Codeforces Global Round 15 C. Maximize the Intersections

题意&#xff1a;给出一个圆&#xff0c;顺时针排布1~2*n&#xff0c;已知连了k条边&#xff0c;问这个圆最好情况下有多少个线的交点&#xff0c;要求线与线之间不能有重复的连接点&#xff0c;也就是每个点只能被一条线连接 思路&#xff1a; 1.考虑没有线的时候&#xff0…

图论:Dijkstra算法

昨天介绍了最小生成树的两个算法&#xff0c;最小生成树的两个算法旨在求解无向有权图中的最小代价联通图的问题&#xff0c;那么对于有向有权图&#xff0c;从起点到终点的最小花费代价问题就可以用 Dijkstra 算法来解决而且Dijkstra算法可以求出来从起始点开始到所有节点的最…

WPFC#超市管理系统(2)顾客管理、供应商管理、用户管理

超市管理系统3. 顾客管理3.1 顾客新增3.2 DataGrid样式3.3 顾客删除3.4 顾客修改4. 供应商管理4.1 供应商管理主界面4.2 新增供应商4.3 修改供应商5. 用户管理5.1 用户管理主界面5.2 新增用户5.3 修改用户总结3. 顾客管理 在CustomerView.xaml使用命令绑定方式添加页面加载Loa…

Windows本地部署DeepSeek

1、Ollama1、下载Ollama安装包https://ollama.com/download&#xff08;如果下载很慢 可以直接找我拿安装包&#xff09;2、使用命令行安装打开cmd 将下载的安装包OllamaSetup.exe 放到想要安装的目录下。&#xff08;如果直接双击&#xff0c;会装到C盘&#xff09;例如想装到…

基于Python的新闻爬虫:实时追踪行业动态

引言 在信息时代&#xff0c;行业动态瞬息万变。金融从业者需要实时了解政策变化&#xff0c;科技公司需要跟踪技术趋势&#xff0c;市场营销人员需要掌握竞品动向。传统的人工信息收集方式效率低下&#xff0c;难以满足实时性需求。Python爬虫技术为解决这一问题提供了高效方…

阿里视频直播解决方案VS(MediaMTX + WebRTC) 流媒体解决方案

背景&#xff1a; 公司采购了新的摄像头&#xff0c;通过rtsp或者rtmp推流到云平台&#xff0c;云平台内部进行转码处理&#xff0c;客户端使用HLS或HTTP-FLV播放&#xff0c;移动App可能使用HLS或私有SDK&#xff0c;超低延时则采用WebRTC。 技术选型&#xff1a; RTSP&…

day33:零基础学嵌入式之网络——TCP并发服务器

一、服务器1.服务器分类单循环服务器&#xff1a;只能处理一个客户端任务的服务器并发服务器&#xff1a;可同时处理多个客户端任务的服务器二、TCP并发服务器的构建1.如何构建&#xff1f;&#xff08;1&#xff09;多进程&#xff08;每一次创建都非常耗时耗空间&#xff0c;…

VR全景制作的流程?VR全景制作可以用在哪些领域?

VR全景制作的流程&#xff1f;VR全景制作可以用在哪些领域&#xff1f;VR全景制作&#xff1a;流程、应用与未来虚拟现实&#xff08;VR&#xff09;全景制作正迅速改变我们的感官体验&#xff0c;使我们能够身临其境地探索虚拟世界&#xff0c;享受沉浸式的奇妙感受。那么&…