1.什么是文件
- 文件是以硬盘为载体的存储在计算机上的信息集合
- 文件可以是文本文档,图片,程序
- 用户进行的输入输出中,以文件为基本单位
- 文件分为有结构文件,无结构文件
2.打开关闭/文件
C语言通过指针对文件流进行访问,指针类型为FILE*,在<stdio.h>中默认提供了三个标准流,可以直接使用不用打开和关闭
文件指针 | 流 | 默认含义 |
---|---|---|
stdin | 标准输入 | 键盘 |
stdout | 标准输出 | 屏幕 |
stderr | 标准错误 | 屏幕 |
2.1 fopen
1 | FILE* fopen(const char* filename, const char* mode);//以mode方式打开路径为filename的文件 |
- filename:文件路径
- mode:文件打开模式
- 返回值:如果打开成功,则返回文件句柄FILE类型的值,如果失败,则返回EOF
2.2 fclose
1 | int fclose(FILE* stream);//关闭程序不再使用的文件,一般和fopen成对出现 |
- stream:打开句柄指针(fopen的返回值)
- 返回值:如果成功关闭返回0,失败返回EOF
2.3 mode分类
1.文本文件(有结构文件)
mode分类 | 含义 |
---|---|
“r”(read) | 以只读的方式打开文件(文件必须存在,否则返回EOF) |
“w”(write) | 打开文件用于写入(文件不存在则创建),清空文件再写入 |
“a”(append) | 打开文件在末尾追加(文件不存在则创建) |
“r+” | 打开文件用于读和写(文件必须存在),从文件头开始覆盖 |
“w+” | 打开文件用于读和写(文件不存在则创建),清空文件再写入 |
“a+” | 打开文件用于读和写(文件不存在则创建),末尾追加 |
2.二进制文件(流式文件)
mode分类 | 含义 |
---|---|
“rb”(read binary) | 以只读的方式打开文件(文件必须存在,否则返回EOF) |
“wb”(write binary) | 打开文件用于写入(文件不存在则创建),清空文件再写入 |
“ab”(append binary) | 打开文件在末尾追加(文件不存在则创建) |
“rb+”/“r+b” | 打开文件用于读和写(文件必须存在),从文件头开始覆盖 |
“wb+”/“w+b” | 打开文件用于读和写(文件不存在则创建),清空文件再写入 |
“ab+”/“a+b” | 打开文件用于读和写(文件不存在则创建),末尾追加 |
3.结果对比
r
1 | int main(int argc,char* argv[]) { |

w
1 | FILE* src = fopen(argv[1], "w");//以全覆盖写的方式打开文件 |

a
1 | FILE* src = fopen(argv[1], "a");//以后缀写的方式打开文件 |

r+
1 | FILE* src = fopen(argv[1], "r+");//以从头覆盖读和写的方式打开文件,文件必须存在 |

w+
1 | FILE* src = fopen(argv[1], "w+");//以全部覆盖读和写的方式打开文件,文件不存在则创建 |

a+
1 | FILE* src = fopen(argv[1], "r+");//以末尾追加写的方式打开文件,可读可写,文件不存在则新建 |

3.读写文件
3.1 fgetc & fputc(字符级的读写)
1 | int fgetc(FILE* stream);//从输入流中读取一个字符 |
- stream:文件句柄指针
- 返回值:如果读取成功返回字符,失败则返回EOF
和getchar()的区别:getchar()只能在标准流stdin中读取一个字符
1 | int fputc(int c,FILE* stream);//向输出流中写入一个字符 |
- c:字符
- stream:文件句柄指针
- 返回值:如果写入成功返回写入字符,失败返回EOF
1 |
|
结果如图:

对比结果:

3.2 fgets & fputs(行级的读写)
fgets
从输入流 stream 中,最多读取 count - 1 个字符,并把读取的字符存入 str 指向的字 符数组中。 fgets 遇到换行符’\n’,或者文件的末尾就会终止(也就是说,读取的字符数可能不足 count - 1 个),并且会存储换行符’\n’。 fgets **会在最后添加空字 符’\0’**。
1 | char* fgets(char* str, int count, FILE* stream); |
参数:
str: 指向一个字符数组
count: 能够写入的最大字符数量(通常是str指向字符数组的长度)
stream: 输入流
返回值:
成功:返回str
失败:NULL
fgets 是 gets 的通用版本,它可以从任意输入流中读取数据,而 gets 只能从 stdin 中读取数据。 fgets 也比 gets 更为安全,因为它限制了读取字符的最大数目 (count - 1)。此外,如果 fgets 是因为读取了换行符而终止,那么它会存储换行 符’\n’,而 gets 函数从来不会存储换行符。
fputs
将 str 指向的字符串,写入输出流 stream 中。
1 | int fputs(const char* str, FILE* stream); |
参数:
str: 要写的字符串(以’\0’结尾的字符串)
stream: 输入流
返回值:
成功:返回一个非负值
失败:EOF
fputs 是 puts 的通用版本,它可以将字符串写入到任意的输出流中,而 puts 只能 写入到 stdout 中。此外, fputs 是原样输出字符串,而 puts 会在字符串后面而外 输出一个换行符’\n’。
1 |
|

3.3 fscanf & fwrite(序列化的读写)
序列化:将数据结构或对象状态转换成可取用格式(例如存成文件,存于缓冲,或经由网络中发送),以留待后续在相同或另一台计算机环境中,能恢复原先状态的过程。 依照序列化格式重新获取字节的结果时,可以利用它来产生与原始对象相同语义的副本。
fscanf
fscanf 和 scanf 类似,是用来进行格式化输入的。
1 | int fscanf(FILE* stream, const char* format, ...) |
scanf 是从标准输入(stdin)中读取数据,而 fscanf 可以从任何一个流中读取数据。也就是说,当 fscanf 的第一个参数为 stdin 时,它的效果等价于 scanf 。顺便提一下, sscanf 可以从字符串中读取数据。
fprintf
fprintf 和 printf 类似,是用来进行格式化输出的。
1 | int fprintf(FILE* stream, const char* format, ...); |
printf 始终是向标准输出(stdout)写入内容的,而 fprintf 可以向任何 一个输出流中写入内容。也就是说,当 fprintf 的第一个参数为 stdout 时,它的效 果等价于 printf 。 顺便提一下, sprintf 可以将内容写入到一个字符数组中。 格式化输入输出,可以用于序列化和反序列化过程中。
示例
1 |
|
结果


3.4 fread & fwrite(二进制文件的读写)
fread 和 fwrite 主要是用来处理二进制文件的。
fread
fread 可以每次读取一大块数据。
1 | size_t fread(void* buffer, size_t size, size_t count, FILE* stream); |
- 参数:
buffer: 指向存放数据的数组(并非只能是数组,也可以是指向一片连续的内存空间的首地址,可以是结构体)
size: 每个元素的大小(以字节为单位)
count: 最多可以读取的元素个数
stream: 输入流
- 返回值:
成功读取元素的个数。当读到文件末尾,或者发生错误时,返回值可能小于count。
我们可以通过feof和ferror函数来判断,到底是读到了文件末尾,还是发生了错误。
fwrite
将存放在 buffer 指向的数组中的 count 个元素写入到输出流 stream 中。
1 | size_t fwrite(const void* buffer, size_t size, size_t count, FILE*stream); |
参数:
buffer: 指向存放数据的数组。
size: 每个元素的大小(以字节为单位)
*count: 要写入元素的个数 *
stream: 输出流
返回值: 成功写入元素的个数。当发生错误时,这个值可能小于count。
示例
1 |
|

序列化/反序列化
1 |
|
结果

4.文件定位
每个流都有相关联的文件位置。在执行读写操作时,文件位置会自动推进,并按照顺序访问文件。顺序访问是很好的,但是有时候,我们可能需要跳跃地访问文件。为此,提供了几个函数来支持这种能力。
1 | int fseek(FILE* stream, long int offset, int whence); |
fseek
fseek 可以改变与 stream 相关联的文件位置。
1 | int fseek(FILE* stream, long int offset, int whence); |
参数:
stream:文件流
whence:表示参照点,参照点有 3 个选择:
SEEK_SET:文件的起始位置。
SEEK_CUR:文件的当前位置。
SEEK_END:文件的末尾位置。
offset:表示偏移量 (可能为负),它是以字节进行计数的。
移动到文件的起始位置,可以这样写:fseek(fp, 0L, SEEK_SET);
移到文件的末尾:fseek(fp,0L,SEEK_END);
往回移动10个字节:fseek(fp,-10L,SEEK_CUR);
- 返回值:
通常情况下会返回0,如果发生错误返回(如位置不存在)返回非0值
ftell
以长整数形式返回当前文件位置,如果发生错误返回-1。
1 | long int filePos = ftell(fp); |
rewind
会将文件位置设置为起始位置,类似于调用:
1 | rewind(fp); |
示例
1 |
|
结果

5.错误处理
errno
错误的检测和处理并不是 C 语言的强项,C 语言没有其它高级语言 (C++, Java, C# 等) 所具有的异常处理机制。C 语言往往是通过函数的返回值,或者是测试 errno 变 量来检测错误的;并且需要程序员自己编写代码来处理错误。
errno 是一个 int 类型的全局变量 (C11 修改为线程本地变量,即每个线程都有一个 独有的 errno 变量),它定义在头文件中。标准库中有些函数 (比如与文件 相关的一些函数),如果在调用过程中发生了错误,它会设置 errno 的值,以表明发生 了何种类型的错误。
程序启动时,会将 errno 的值设为 0,表示没有错误发生。其它非 0 值都表示发生了 某种类型的错误。我们可以通过 perror 和 strerror 来显示错误信息。其中, perror 定义在 头文件中, strerror 定义在 头文件中。
1 |
|
结果

- 本文作者: 迪丽惹Bug
- 本文链接: https://lyroom.github.io/2024/04/11/文件详解/
- 版权声明: 本博客所有文章除特别声明外,均采用 MIT 许可协议。转载请注明出处!