首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >收集飞花令碎片——【C语言】文件操作

收集飞花令碎片——【C语言】文件操作

作者头像
枫亭湖区
发布2025-11-13 09:16:51
发布2025-11-13 09:16:51
1690
举报

一、什么是文件

文件是计算机中用于存储数据的基本单位,可以理解为存储在计算机外部存储器(如硬盘、U盘等)上的数据集合

二、为什么需要文件操作?

文件操作就是让你的程序能和硬盘上的数据互动起来的关键 C 语言中,文件操作主要依赖于 stdio.h 头文件中的一系列函数,核心概念是文件指针(FILE *)和流(stream

三、文件分类

我们一般谈的文件有两种:程序文件、数据文件(从文件功能的角度来分类的)

1)程序文件

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

2)数据文件

  • 根据数据的组织形式,数据文件被分为文本文件二进制文件
    • 数据在内存中以二进制的形式存储,如果不加转换的输出到外存的文件中,就是二进制文件
    • 如果要求在外存上以ASCII码的形式存储,则需要在存储前转换。以ASCII字符的形式存储的文件就是文本文件

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

四、文件名

  • 一个文件要有一个唯一的文件标识,以便用户识别和引用。
  • 文件名包含3部分:文件路径+文件名主干+文件后缀 例如: c:\code\test.txt
  • 为了方便起见,文件标识常被称为文件名

五、文件是如何存储的?

⼀个数据在文件中是怎么存储的呢? 字符⼀律以ASCII形式存储数值型数据既可以⽤ASCII形式存储 ,也可以使⽤二进制形式存储 如有整数10000,如果以ASCII码的形式输出到磁盘,则磁盘中占⽤5个字节(每个字符⼀个字节),⽽ ⼆进制形式输出,则在磁盘上只占4个字节

在这里插入图片描述
在这里插入图片描述

六、文件的关键知识

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


6.1)标准流

标准流是C语言标准库中预定义的、在程序启动时自动打开的流。 它们为程序与外部环境(通常是终端控制台)进行输入输出提供了一个标准化的接口。程序员无需手动执行打开文件等操作,就可以直接使用这些流进行读写。


6.1.1)三种标准流
  • C语言主要定义了三种标准流:
  1. 标准输入
  • 文件指针: stdin
  • 用途: 用于从程序外部读取数据。默认情况下,它连接到键盘。
  • 常用函数: scanf()getchar()gets()(已废弃) 等函数都从 stdin 读取数据。
  1. 标准输出
  • 文件指针: stdout
  • 用途: 用于向程序外部输出正常数据。默认情况下,它连接到屏幕(控制台)。
  • 常用函数: printf()putchar()puts() 等函数都向 stdout 输出数据。
  1. 标准错误
  • 文件指针: stderr
  • 用途: 用于输出错误消息和诊断信息。默认情况下,它也连接到屏幕。
  • 常用函数: fprintf(stderr, …)、perror() 等函数专门向 stderr 输出数据

这是默认打开了这三个流,我们使用scanfprintf等函数就可以直接进⾏输入输出操作 stdinstdoutstderr 三个流的类型是: FILE * ,通常称为文件指针 C语言中,就是通过 FILE* 的文件指针来维护流的各种操作的


6.2)文件的打开和关闭

在编写程序的时候,在打开文件的同时, 都会返回一个FILE*的指针变量指向该文件, 也相当于建立了指针和文件的关系

要使用这两个函数,你必须包含头文件:#include <stdio.h>

  • 函数原型
代码语言:javascript
复制
FILE *fopen(const char *filename, const char *mode);
  • 功能: fopen 函数是用来打开参数 filename 指定的文件,同时将打开的文件和一个流进行关联,后续对流的操作是通过 fopen 函数返回的指针来维护, 具体对流(关联的文件)的操作是通过参数 mode 来指定的
  • 参数: filename : 表示被打开的文件的名字,这个名字可以绝对路径,也可以是相对路径。 mode : 表示对打开的文件的操作⽅式,具体见下⾯的表格。
  • 返回值: • 若文件成功打开,该函数将返回⼀个指向 FILE 对象的指针,该指针可用于后续操作中标识对应的流。 • 若打开失败,则返回NULL指针,所以⼀定要fopen的返回值做判断,来验证文件是否打开成功
在这里插入图片描述
在这里插入图片描述
  • 代码示例
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • 函数原型
代码语言:javascript
复制
int fclose ( FILE * stream );
  • 功能: 关闭参数stream关联的文件,并取消其关联关系。 与该流关联的所有内部缓冲区均会解除关联并刷新:任何未写入的输出缓冲区内容将被写入,任何未读取的输入缓冲区内容将被丢弃
  • 参数: stream:指向要关闭的流的FILE对象的指针
  • 返回值: 成功关闭stream指向的流会返回 0,否则会返回 EOF
  • 代码示例
在这里插入图片描述
在这里插入图片描述

6.3)文件指针

缓冲文件系统中,关键的概念是 “文件类型指针” ,简称 “文件指针”

每个被使用的文件都在内存中开辟了一个相应的文件信息区,用来存放文件的相关信息(如文件的名字,文件状态及文件当前的位置等)。这些信息是保存在一个结构体变量中的。 该结构体类型是由系统声明的,取名 FILE

在这里插入图片描述
在这里插入图片描述
  • 每当打开一个文件的时候,系统会根据文件的情况自动创建一个FILE结构的变量,并填充其中的信息,使用者不必关心细节
  • 一般都是通过一个FILE的指针来维护这个FILE结构的变量,这样使用起来更加方便

下面我们创建一个FILE*的指针变量

代码语言:javascript
复制
FILE* pf;//⽂件指针变量

声明了一个变量: 这个变量的名字是pf 指定了变量的类型: 这个变量的类型是 FILE* 通过文件指针变量能够间接找到与它关联的文件


6.4)文件的顺序读写

函数名

功能

适用于

fgetc

从输入流中读取一个字符

所有输入流

fputc

向输出流中写入一个字符

所有输出流

fgets

从输入流中读取一个字符串

所有输入流

fputs

向输出流中写入一个字符串

所有输出流

fscanf

从输入流中读取带有格式的数据

所有输入流

fprintf

向输出流中写入带有格式的数据

所有输出流

fread

从输入流中读取一块数据

文件输入流

fwrite

向输出流中写入一块数据

文件输出流

6.4.1)fgetc函数
  • 作用 从输入流中读取一个字符。
代码语言:javascript
复制
int fgetc(FILE *stream);
  • stream:指向要读取的 FILE 对象的指针(例如,stdinfopen() 返回的指针)。
  • 此函数从指定的流中读取下一个无符号字符,并将其作为 int 类型返回。
  • 流的内部文件位置指示器会前进一个字符。
  • 返回值:成功时,返回读取的字符(提升为 int 类型)。如果到达文件末尾(End-Of-File)或发生读取错误,它会返回 EOF
  • 代码示例
在这里插入图片描述
在这里插入图片描述

6.4.2)fputc函数
  • 作用 向输出流中写入一个字符。
代码语言:javascript
复制
int fputc(int character, FILE *stream);
  • character 要写入的字符,以 int 类型传递。
  • stream 指向要写入的 FILE 对象的指针(例如,stdoutfopen() 返回的指针)。
  • 此函数将 character(转换为 unsigned char)写入指定流,并推进文件位置指示器。
  • 返回值: 成功时,返回写入的字符(int 类型) 如果发生错误,返回 EOF
  • 代码示例:
在这里插入图片描述
在这里插入图片描述

6.4.3)fgets函数
  • 作用 从输入流中读取一个字符串(一行)
代码语言:javascript
复制
char *fgets(char *str, int n, FILE *stream);
  • str: 指向一个字符数组的指针,读取的字符串将存储在这里。
  • n: 最多读取 n-1 个字符(为末尾的 \0 留出空间)。
  • stream: 要读取的输入流。
  • fgets: 会读取字符,直到 (1) 读取了 n-1 个字符,(2) 读到了一个换行符 \n,或 (3) 遇到了文件结尾 EOF。
  • 重要特性: fgets 会包含它读到的换行符 \n(如果读到了的话),并在字符串末尾自动添加一个空终止符 \0。
  • 返回值: 成功时,返回 str 指针。如果到达文件末尾且没有读取任何字符,或发生错误,返回 NULL
  • 代码示例
在这里插入图片描述
在这里插入图片描述

6.4.4)fputs函数
  • 作用 向输出流中写入一个字符串
代码语言:javascript
复制
int fputs(const char *str, FILE *stream);
  • str: 指向要写入的(以 \0 结尾的)字符串的指针。
  • stream: 要写入的输出流。
  • 此函数会将 str 指向的字符串写入流中,但它不会自动添加换行符\n。它会一直写到 \0 为止(\0 本身不会被写入)。
  • 返回值: 成功时,返回一个非负值。失败时,返回 EOF
  • 代码示例
在这里插入图片描述
在这里插入图片描述

6.4.5)fscanf函数
  • 作用 从输入流中读取带格式的数据
代码语言:javascript
复制
int fscanf(FILE *stream, const char *format, ...);
  • stream:要读取的输入流。
  • format:格式控制字符串,与scanf相同 (例如 “%d %s”)
  • ...:可变参数,必须是指针,用于接收读取到的数据。
  • fscanf 会根据 format 字符串的指示,从 stream 中解析数据,并将数据存入后续参数指向的内存地址中。
  • 返回值: 成功匹配并赋值的项数。如果到达文件末尾或出错,可能返回 0EOF
  • 代码示例
在这里插入图片描述
在这里插入图片描述

6.4.6)fprintf函数
  • 作用 向输出流中写入带格式的数据
代码语言:javascript
复制
int fprintf(FILE *stream, const char *format, ...);
  • stream 要写入的输出流。
  • format 格式控制字符串,与 printf 相同。
  • ... 可变参数,即要格式化并写入的数据。
  • fprintf 的工作方式与 printf 完全相同,只是 printf 默认写入到 stdout,而 fprintf 写入到你指定的 stream
  • 返回值: 成功时,返回写入的总字符数;失败时,返回一个负数。
  • 代码示例
在这里插入图片描述
在这里插入图片描述

6.4.7)fread函数
  • 作用 从(文件)输入流中读取一块数据(二进制)
代码语言:javascript
复制
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
  • ptr 指向用于存储读取数据的内存块的指针(例如一个数组或结构体)。
  • size 要读取的每个元素的字节大小(例如 sizeof(int))。
  • nmemb 要读取的元素个数。
  • stream 要读取的输入流。
  • 此函数总共会尝试读取 size * nmemb 个字节。它主要用于二进制I/O
  • 返回值: 返回成功读取的元素个数(即 nmemb 的数量)。如果这个数字小于 nmemb,则表示可能到达了文件末尾或发生了错误
  • 代码示例
在这里插入图片描述
在这里插入图片描述

6.4.8)fwrite函数
  • 作用 向(文件)输出流中写入一块数据(二进制)
代码语言:javascript
复制
size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);
  • ptr: 指向包含要写入数据的内存块的指针。
  • size: 要写入的每个元素的字节大小。
  • nmemb: 要写入的元素个数。
  • stream: 要写入的输出流。
  • 此函数用于将内存中的size * nmemb个字节(通常是二进制数据)写入文件。
  • 返回值: 返回成功写入的元素个数。如果小于nmemb,则表示发生了错误
  • 代码示例
在这里插入图片描述
在这里插入图片描述

6.5)拓展:sprintf和sscanf

对比一组函数

scanf/fscanf/sscanf printf/fprintf/sprintf

6.5.1)sprintf
  • 作用 将格式化的数据打印到一个字符串(字符数组)中而不是打印到控制台或文件
代码语言:javascript
复制
int sprintf(char *str, const char *format, ...);
  • str (字符串): 这是一个指向目标字符数组(缓冲区)的指针。格式化后的结果将存储在这里
  • format (格式化): 格式控制字符串,与 printf 完全相同(例如 “%d %s”)
  • ... (可变参数): 要格式化的数据(变量、常量等)
  • 返回值 (int): 返回成功写入到str中的字符总数,不包括末尾自动添加的\0空终止符。如果发生错误,返回一个负数
  • ⚠️ 重要安全警告: sprintf 非常危险,因为它不检查目标缓冲区 str 是否有足够的空间。如果格式化后的字符串长度超过了str的大小,就会发生缓冲区溢出 (Buffer Overflow),这是 C 语言中最严重的安全漏洞之一

在现代 C 编程中,强烈建议使用 snprintf 代替 sprintf snprintf (更安全):

代码语言:javascript
复制
int snprintf(char *str, size_t n, const char *format, ...);

n: 它额外增加了一个参数 n,表示 str 缓冲区的最大容量。snprintf 保证写入的字符数(包括 \0)绝对不会超过 n,从而避免了溢出

  • 代码示例
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

6.5.2)sscanf
  • 作用 从一个已有的字符串中,按照格式解析数据,并将解析出的数据存入变量中
代码语言:javascript
复制
int sscanf(const char *str, const char *format, ...);
  • str (字符串): 这是你要从中读取数据的源字符串(例如 const char *data = "Age: 30";)。
  • format (格式化): 格式控制字符串,与 scanf 完全相同(例如 “%d %s”)。
  • ... (可变参数): 必须是指针,用于接收解析出来的数据(例如 &my_var)。
  • 返回值 (int): 返回成功匹配并赋值的项数。如果一开始就匹配失败,返回 0。如果发生错误或在任何数据被成功读取前就到达了字符串末尾,返回 EOF
  • 代码示例
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

6.6)文件的随机读写

允许你立即跳转到文件中的任意指定位置进行读取写入


6.6.1)fseek():设置文件位置指示器
  • 手动设置文件位置指示器的位置
代码语言:javascript
复制
int fseek(FILE *stream, long offset, int whence);
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

示例用途:

  • 跳转到文件开头:fseek(fp, 0L, SEEK_SET);
  • 跳过文件的前 100 个字节:fseek(fp, 100L, SEEK_SET);
  • 向前跳跃50 个字节:fseek(fp, -50L, SEEK_CUR);
  • 跳转到文件末尾:fseek(fp, 0L, SEEK_END);
  • 跳转到距离文件末尾 10 个字节的位置:fseek(fp, -10L, SEEK_END);

返回值:

  • 成功时返回 0
  • 失败时返回非零值(通常是由于在文本模式下尝试进行非法定位或文件不支持等原因)

6.6.2)ftell():获取当前文件位置
  • 用于查询文件位置指示器当前相对于文件开头的字节偏移量
代码语言:javascript
复制
long ftell(FILE *stream);
  • 🌟 参数详解
在这里插入图片描述
在这里插入图片描述

返回值:

  • 成功时返回一个 long 类型的整数,表示当前位置相对于文件开头的字节偏移量。
  • 失败时返回 -1L

示例用途:

ftell() 最常用的两个场景是:

  • 保存当前位置: 在进行临时跳转之前,记录当前位置,以便之后能使用 fseek 准确返回。
  • 获取文件大小:fseek(fp, 0L, SEEK_END); 跳转到文件末尾,然后 size = ftell(fp); 即可得到文件总字节数
代码语言:javascript
复制
// 示例:保存当前位置
long current_pos = ftell(fp);
// ... 进行一些其他读写操作 ...
// 恢复到之前的位置
fseek(fp, current_pos, SEEK_SET);

6.6.3)rewind():重置文件位置
  • 将文件位置指示器重置到文件开头
代码语言:javascript
复制
void rewind(FILE *stream);
  • 🌟 参数详解
在这里插入图片描述
在这里插入图片描述

功能:

  • rewind(fp) 的效果完全等同于执行以下两个操作:
    • fseek(fp, 0L, SEEK_SET);(将位置设为文件开头)
    • 清除文件流的错误指示器(如果之前有错误发生)==

返回值:

  • void (无返回值)。

七、文件缓冲区

🚀 缓冲文件系统详解

  • 缓冲文件系统的核心目的在于解决 内存操作速度(快)与磁盘 I/O 速度(慢) 之间的巨大矛盾,通过引入文件缓冲区来减少程序与慢速磁盘之间直接交互的次数,从而大幅提高效率
在这里插入图片描述
在这里插入图片描述

fflush

  • 作用 强制将文件缓冲区中的所有待写入数据立即写入到实际的文件或设备中
代码语言:javascript
复制
int fflush ( FILE * stream );

功能: 强制刷新参数 stream 指定流的缓冲区,确保数据写⼊底层设备。

  • 输出流:将缓冲区中未写⼊的数据⽴即写⼊⽂件。
  • 输⼊流:⾏为由具体实现决定,⾮C语⾔标准⾏为(可能清空输⼊缓冲区)
  • 参数为 NULL 时:刷新所有打开的输出流

参数: stream :指向⽂件流的指针(如 stdout 、文件指针等)

返回值:成功返回 0 ,失败返回 EOF

  • 注意事项:
  1. 仅对输出流更新流(最后⼀次操作为输出)有明确刷新⾏为
  2. 输⼊流的刷新⾏为不可移植(如清空输⼊缓冲区是⾮标准特性)
  3. 程序正常终⽌( exit )或调⽤ fclose 时会⾃动刷新,但程序崩溃时缓冲区数据可能丢失
在这里插入图片描述
在这里插入图片描述

如果你觉得文章对你有帮助,请给文章一个三连吧

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2025-11-12,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、什么是文件
  • 二、为什么需要文件操作?
  • 三、文件分类
    • 1)程序文件
    • 2)数据文件
  • 四、文件名
  • 五、文件是如何存储的?
  • 六、文件的关键知识
    • 6.1)标准流
      • 6.1.1)三种标准流
    • 6.2)文件的打开和关闭
    • 6.3)文件指针
    • 6.4)文件的顺序读写
      • 6.4.1)fgetc函数
      • 6.4.2)fputc函数
      • 6.4.3)fgets函数
      • 6.4.4)fputs函数
      • 6.4.5)fscanf函数
      • 6.4.6)fprintf函数
      • 6.4.7)fread函数
      • 6.4.8)fwrite函数
    • 6.5)拓展:sprintf和sscanf
      • 6.5.1)sprintf
      • 6.5.2)sscanf
    • 6.6)文件的随机读写
      • 6.6.1)fseek():设置文件位置指示器
      • 6.6.2)ftell():获取当前文件位置
      • 6.6.3)rewind():重置文件位置
  • 七、文件缓冲区
    • 🚀 缓冲文件系统详解
    • fflush
  • 如果你觉得文章对你有帮助,请给文章一个三连吧
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档