1. 为什么使用文件?
在程序设计中,使用文件的主要原因是为了实现数据的持久存储和高效管理。
-
持久存储数据:程序运行时产生的数据(如用户输入、计算结果)通常存储在内存中,但内存是易失性的(断电后数据丢失)。文件允许将数据保存在外部存储设备(如硬盘、SSD)上,确保数据长期可用。例如,一个文本编辑器需要将用户输入的内容保存到文件中,以便下次打开时恢复。
-
数据共享和传输:文件作为标准的数据容器,便于在不同程序或系统间交换信息。例如,一个数据库程序可以导出数据到文件(如 CSV 格式),另一个程序(如 Excel)可以导入该文件进行处理。
-
程序间通信:文件可以作为中介,实现进程间通信。例如,多个程序可以读写同一个文件来协调任务(如日志文件记录系统事件)。
总之,文件解决了数据易失性问题,支持跨平台和跨程序的数据操作,是程序设计中的基础工具。
2. 什么是文件?
在程序设计中,文件是一个存储在外部存储设备上的命名数据集合,用于组织和管理信息。文件可以是文本、二进制数据或其他格式的序列。从功能角度,文件主要分为两类:程序文件 和数据文件。这种分类基于文件在系统中的角色:
2.1 程序文件
程序文件直接参与程序的创建、编译和执行过程。它们包含代码或指令,使计算机能运行特定任务。常见类型包括:
-
源程序文件(后缀为
.c) :这是程序员编写的源代码文件,包含人类可读的代码(如 C 语言代码)。源文件是编译过程的起点,重点在于可编辑性和可维护性。例如,一个.c文件定义了程序逻辑,编译器将其转换为机器码。 -
目标文件(在 Windows 环境中后缀为
.obj) :这是编译后的中间文件,包含机器码但尚未链接。目标文件是源文件编译的结果,用于生成最终可执行程序。关键点:它支持模块化编程,多个.obj文件可以链接成一个程序。 -
可执行程序(在 Windows 环境中后缀为
.exe) :这是可直接运行的程序文件,由目标文件链接生成。可执行文件包含完整的机器指令,操作系统加载并执行它。例如,双击.exe文件启动应用程序。
程序文件的重点是支持程序的开发和运行流程,从源代码到可执行实体。
2.2 数据文件
数据文件用于存储程序处理或生成的数据,而不是代码本身。它们是程序输入或输出的载体。关键点:
-
功能 :数据文件保存用户数据(如文档、图像、数据库),使程序能读写外部信息。例如,一个文本编辑器创建的
.txt文件存储用户输入的文字。 -
格式多样 :数据文件可以是文本格式(如
.txt,.csv)或二进制格式(如.jpg,.dat)。文本文件易读但效率低,二进制文件高效但需特定程序解析。 -
重要性:数据文件实现数据持久化,避免每次运行程序时重新输入数据。它们也支持数据分析、备份和共享。
数据文件的重点在于内容而非结构,是程序与用户数据交互的桥梁。
2.3 文件名
文件名是文件的唯一标识符,用于用户和系统识别文件。它由三部分组成:文件路径 + 文件名主干 + 文件后缀。关键点:
-
文件路径:指定文件在存储设备上的位置。路径可以是绝对路径或相对路径:
- 绝对路径 :从根目录开始的完整路径,独立于当前目录。例如,在 Windows 中,
C:\Users\Name\Documents\file.txt明确指定文件位置。 - 相对路径 :基于当前工作目录的路径,更简洁。例如,如果当前目录是
C:\Users\Name\,则Documents\file.txt表示相对路径。相对路径便于在脚本或程序中灵活引用文件。
- 绝对路径 :从根目录开始的完整路径,独立于当前目录。例如,在 Windows 中,
-
文件名主干 :文件的主要名称,由用户自定义(如
report在report.docx中)。它应简洁且描述性强。 -
文件后缀 :也称为文件扩展名,表示文件类型(如
.txt表示文本文件)。后缀帮助系统和用户识别文件格式(如.exe表示可执行文件)。
特殊路径符号:
.(一个点) :表示当前目录。例如,路径.\data.txt指当前文件夹下的data.txt文件。..(两个点) :表示上一级目录。例如,路径..\parent\file.txt指从当前目录向上跳一级,再进入parent目录访问文件。
文件名的重要性在于确保文件唯一性和可访问性。路径管理(如使用相对路径)能简化文件操作,提高程序可移植性。
3. 二进制文件和文本文件的解释
根据数据的组织形式,数据文件主要分为文本文件和二进制文件。
3.1 二进制文件和文本文件的基本概念
- 数据在内存中的存储 :所有数据在计算机内存中都以二进制形式存储。例如,一个整数变量(如
int a = 10000;)在内存中直接表示为二进制序列,而不是人类可读的字符。 - 二进制文件:如果数据不加任何转换地输出到外部存储设备(如硬盘),文件就是二进制文件。这意味着文件内容直接复制内存中的二进制数据,没有经过编码处理。二进制文件通常用于存储程序数据、图像、音频等,因为效率高且节省空间。
- 文本文件:如果数据在存储前被转换为ASCII码(一种字符编码标准),文件就是文本文件。文本文件的内容是可读的字符序列,例如数字10000在文本文件中会被存储为字符'1'、'0'、'0'、'0'、'0',每个字符占用1字节(ASCII码形式)。文本文件常用于配置文件、日志等需要人类可读的场景。
关键区别:
- 存储方式:二进制文件直接存储原始二进制数据;文本文件存储ASCII编码后的字符。
- 文件大小:二进制文件通常更小,因为它不添加额外编码(如10000在二进制文件中可能只占4字节,而在文本文件中占5字节)。
- 可读性:文本文件可直接用文本编辑器查看;二进制文件需要特定工具解析。
3.2 详细解释C语言代码
您提供的代码演示了如何将一个整数以二进制形式写入文件。我将逐行分析代码,并解释每个函数的作用。
c
int main() {
int a = 10000; // 定义一个整数变量a,并初始化为10000
FILE* pf = fopen("test.txt", "wb"); // 打开文件"test.txt",以二进制写入模式
fwrite(&a, 4, 1, pf); // 以二进制形式将数据写入文件
fclose(pf); // 关闭文件
pf = NULL; // 将文件指针设为NULL,避免悬空指针
return 0;
}
代码逐步解释:
-
变量定义:
int a = 10000;:这行代码定义了一个整数变量a,并赋值为10000。在内存中,a以二进制形式存储。例如,假设int类型占4字节(32位系统),10000的二进制表示为10011100010000210011100010000_2100111000100002(实际存储为小端字节序)。
-
打开文件:
FILE* pf = fopen("test.txt", "wb");:fopen函数用于打开文件。第一个参数是文件名("test.txt"),第二个参数是模式字符串。- 模式"wb"表示以二进制写入(write binary)方式打开文件。如果文件不存在,则创建新文件;如果存在,则覆盖原有内容。
FILE* pf声明一个文件指针pf,用于后续操作文件。
-
写入数据:
fwrite(&a, 4, 1, pf);:fwrite函数用于以二进制形式写入数据到文件。参数解释如下:- 第一个参数
&a:这是数据源的地址。&a表示变量a的内存地址(指针),指向存储10000的二进制数据。 - 第二个参数
4:表示每个数据元素的大小(字节数)。这里设置为4,因为int类型通常占4字节(取决于系统)。 - 第三个参数
1:表示要写入的元素数量。这里设置为1,因为只写入一个整数。 - 第四个参数
pf:文件指针,指定写入的目标文件。
- 第一个参数
- 这行代码的作用是:直接将内存中
a的二进制内容(4字节)写入文件,不加任何转换。因此,文件"test.txt"的内容将是二进制序列,而不是可读的字符。
-
关闭文件和处理指针:
fclose(pf);:关闭文件。这是一个重要步骤,确保所有缓冲数据写入磁盘并释放资源。pf = NULL;:将文件指针设为NULL,防止后续代码误用已关闭的指针(避免悬空指针错误)。
代码重点:
- 二进制写入 :通过
fwrite函数,数据直接从内存复制到文件,不进行ASCII转换。这与文本文件写入(如使用fprintf)不同,后者会将数字转换为字符。 - 文件模式:模式字符串"wb"明确指定二进制写入。如果使用"w"(文本模式),写入时会进行隐式转换。
- 实际效果:运行此程序后,"test.txt"文件将包含4字节的二进制数据(如十六进制表示可能为0x10 0x27 0x00 0x00,取决于字节序),而不是文本"10000"。您可以用二进制查看器(如hex编辑器)验证内容。
3.3 总结与重点强调
- 核心区别:二进制文件存储原始二进制数据,效率高但不可直接阅读;文本文件存储ASCII字符,可读但效率较低。
- 代码关键点 :
- 使用
fwrite和"wb"模式实现二进制写入,避免数据转换。 - 整数等数据类型直接以内存格式存储,文件大小和内容取决于数据类型(如
int大小)。
- 使用
- 实际应用 :二进制文件适用于需要高效存储和读取的场景(如数据库、图像处理);文本文件适用于人类可读的场景(如配置文件)。在C语言中,正确选择文件模式(如"wb" vs "w")和写入函数(如
fwritevsfprintf)至关重要。
4. 详细解释:C语言中的文件打开与关闭
4.1 流和标准流
在C语言中,流(Stream) 是数据输入/输出的抽象概念,它将物理设备(如键盘、显示器)或文件抽象为连续的字节序列。
-
标准流(Standard Streams):C程序启动时自动打开三个预定义流:
stdin(标准输入流) :通常关联键盘输入。函数如scanf从stdin读取数据。stdout(标准输出流) :通常关联显示器输出。函数如printf向stdout写入数据。stderr(标准错误流):专用于错误消息输出,也关联显示器,确保错误信息不被重定向干扰。
这三个流的类型均为
FILE*(文件指针),可直接用于输入/输出操作。例如:cint num; scanf("%d", &num); // 从 stdin 读取输入 printf("Value: %d", num); // 向 stdout 输出 perror("Error"); // 向 stderr 输出错误
4.2 文件指针
文件操作的核心是 文件指针(File Pointer) ,类型为 FILE*。FILE 是一个系统定义的结构体(在 <stdio.h> 中声明),存储文件的元信息(如文件名、状态、当前位置等)。每个打开的文件在内存中有一个对应的 FILE 结构体实例。
-
作用 :文件指针(如
FILE* pf;)指向这个结构体,通过它间接访问文件。例如:cFILE* pf; // 声明文件指针变量当文件打开后,
pf指向该文件的FILE结构体,所有文件操作(读/写)都通过pf进行。
4.3 文件的打开与关闭
文件操作遵循"打开 → 读写 → 关闭"的流程:
-
打开文件 :使用
fopen函数。cFILE *fopen(const char *filename, const char *mode);- 参数 :
filename:文件路径(绝对或相对路径,如"test.txt")。mode:操作模式(如"r"只读),详见下文表格。
- 返回值 :
- 成功:返回指向
FILE结构的指针。 - 失败:返回
NULL(必须检查,否则可能导致程序崩溃)。
- 成功:返回指向
- 关键点 :
fopen将文件与流关联,后续操作通过返回的指针进行。
- 参数 :
-
关闭文件 :使用
fclose函数。cint fclose(FILE *stream);- 作用 :释放资源并断开流关联。失败时返回
EOF(通常忽略,但需处理错误)。 - 最佳实践 :关闭后设置指针为
NULL,避免野指针。
- 作用 :释放资源并断开流关联。失败时返回
代码示例详解
用户提供的代码演示了基本文件操作:
c
#include <stdio.h> // 必需头文件
int main() {
// 尝试以只读模式打开文件 "test.txt"
FILE* pf = fopen("test.txt", "r"); // "r" 模式表示只读
// 错误处理:检查 fopen 是否成功
if (pf == NULL) {
perror("fopen"); // 输出错误信息(如文件不存在)
return 1; // 返回非零值表示错误
} else {
printf("打开文件成功\n"); // 成功打开
}
// 此处可添加文件读写操作(如 fread/fscanf)
// 关闭文件
fclose(pf); // 释放资源
pf = NULL; // 避免野指针
return 0; // 正常退出
}
- 重点解释 :
fopen("test.txt", "r"):尝试打开当前目录下的test.txt文件。模式"r"要求文件必须存在。if (pf == NULL):必须检查返回值。如果文件不存在或权限不足,fopen返回NULL。perror("fopen"):输出错误原因(如fopen: No such file or directory)。fclose(pf):关闭文件后,pf不再有效,设置为NULL是良好习惯。
4.4 文件操作模式(mode)详解
fopen 的 mode 参数定义了文件的访问方式。以下是基于C语言代码的解释,展示不同模式的行为。
代码示例解释不同模式
-
"r"(只读文本文件) :文件必须存在,否则失败。
cFILE* file = fopen("data.txt", "r"); // 只读打开 if (file == NULL) { perror("Failed to open"); // 文件不存在时输出错误 } -
"w"(只写文本文件) :文件不存在则创建;存在则清空内容。
cFILE* file = fopen("output.txt", "w"); // 只写打开 if (file != NULL) { fprintf(file, "New content"); // 写入数据(覆盖原有内容) } -
"a"(追加文本文件) :文件不存在则创建;存在则在末尾添加数据。
cFILE* file = fopen("log.txt", "a"); // 追加模式 if (file != NULL) { fprintf(file, "New log entry\n"); // 在文件尾添加 } -
"rb"(只读二进制文件) :类似
"r",但用于二进制文件(如图片)。cFILE* file = fopen("image.bin", "rb"); // 二进制只读 -
"r+"(读写文本文件) :文件必须存在,支持读取和写入。
cFILE* file = fopen("data.txt", "r+"); // 读写模式 if (file != NULL) { char buffer[100]; fscanf(file, "%s", buffer); // 读取 fseek(file, 0, SEEK_SET); // 移动指针到开头 fprintf(file, "Updated"); // 写入 } -
"w+"(读写文本文件) :文件不存在则创建;存在则清空。支持读写。
cFILE* file = fopen("temp.txt", "w+"); // 读写+清空 -
其他模式 (如
"ab+")逻辑类似,结合二进制和追加特性。
文件操作模式汇总表
以下表格总结了所有 fopen 模式的行为,基于C标准库规范:
| Mode | 含义 | 文件不存在时的行为 | 文件存在时的行为 |
|---|---|---|---|
"r" |
只读(文本文件) | 出错 (返回 NULL) |
打开文件,从头读取 |
"w" |
只写(文本文件) | 创建新文件 | 清空内容,从头写入 |
"a" |
追加(文本文件) | 创建新文件 | 打开文件,从末尾写入 |
"rb" |
只读(二进制文件) | 出错 (返回 NULL) |
打开文件,从头读取 |
"wb" |
只写(二进制文件) | 创建新文件 | 清空内容,从头写入 |
"ab" |
追加(二进制文件) | 创建新文件 | 打开文件,从末尾写入 |
"r+" |
读写(文本文件) | 出错 (返回 NULL) |
打开文件,可读写(指针在开头) |
"w+" |
读写(文本文件) | 创建新文件 | 清空内容,可读写(指针在开头) |
"a+" |
读写(文本文件),在文件尾操作 | 创建新文件 | 打开文件,可读写(指针在末尾) |
"rb+" |
读写(二进制文件) | 出错 (返回 NULL) |
打开文件,可读写(指针在开头) |
"wb+" |
读写(二进制文件) | 创建新文件 | 清空内容,可读写(指针在开头) |
"ab+" |
读写(二进制文件),在文件尾操作 | 创建新文件 | 打开文件,可读写(指针在末尾) |
表格说明:
- 文本 vs 二进制 :文本模式(无
b)处理换行符转换(如\n→\r\n在 Windows);二进制模式(带b)直接读写字节。 - 读写位置 :
"r+"和"w+"初始位置在文件开头;"a+"和"ab+"初始位置在文件末尾。 - 安全提示 :始终检查
fopen返回值,避免未定义行为。关闭文件使用fclose防止资源泄漏。
4.5 总结与最佳实践
- 核心流程 :打开(
fopen)→ 操作(读写)→ 关闭(fclose)。 - 错误处理 :必须验证
fopen返回值,用perror诊断错误。 - 模式选择 :根据需求选择
mode。例如:- 读取现有文件:用
"r"或"rb"。 - 创建新文件或覆盖:用
"w"或"wb"。 - 日志追加:用
"a"或"ab"。
- 读取现有文件:用
- 二进制文件 :处理非文本数据(如图像)时,使用带
b的模式避免编码问题。 - 资源管理 :关闭文件后设置指针为
NULL,增强代码健壮性。
5. 件顺序读写
在C语言编程中,文件操作是处理数据输入输出的核心部分。
函数对比表格
为突出不同函数的适用场景,以下表格对比关键函数:
| 函数名 | 功能 | 适用流或目标 | 格式化与否 | 主要用途 |
|---|---|---|---|---|
fputc |
写入一个字符 | 所有输出流 | 否 | 逐字符输出 |
fgetc |
读取一个字符 | 所有输入流 | 否 | 逐字符输入 |
fputs |
写入一个字符串 | 所有输出流 | 否 | 字符串输出 |
fgets |
读取一个字符串 | 所有输入流 | 否 | 行输入(包含换行符) |
fprintf |
写入格式化数据 | 所有输出流 | 是 | 结构化数据输出 |
fscanf |
读取格式化数据 | 所有输入流 | 是 | 结构化数据输入 |
fwrite |
写入数据块 | 文件输出流(二进制) | 否 | 二进制数据输出 |
fread |
读取数据块 | 文件输入流(二进制) | 否 | 二进制数据输入 |
sprintf |
格式化数据到字符串 | 内存缓冲区 | 是 | 字符串生成 |
sscanf |
从字符串解析格式化数据 | 内存缓冲区 | 是 | 字符串解析 |
printf |
写入格式化数据到stdout | stdout | 是 | 控制台输出 |
scanf |
从stdin读取格式化数据 | stdin | 是 | 控制台输入 |
表格说明:
- 格式化与否 :指是否支持格式说明符(如
%d)。 - 适用流 :
所有输出流包括文件和stdout;文件流特指文件操作。 - 主要用途 :基于常见场景,如
fread/fwrite适合二进制文件,fgets/fputs适合文本文件。
5.1 fputc:向输出流写入一个字符
- 功能:将指定字符写入到输出流(如文件或stdout)中。写入后,流的位置指示器自动前进一个位置。
- 参数 :
character:要写入的字符(以int类型传递)。stream:指向输出流的FILE*指针(例如文件指针或stdout)。
- 返回值 :
- 成功时:返回写入的字符(
int类型)。 - 失败时:返回
EOF(-1),并设置错误指示器(可通过ferror()检查)。
- 成功时:返回写入的字符(
- 重点 :
- 适用于所有输出流,包括文件和标准输出。
- 常用于写入单个字符,如日志记录或字符处理。
- 需确保流已以写入模式打开。
C语言代码示例:
c
#include <stdio.h>
int main() {
FILE *file = fopen("example.txt", "w"); // 打开文件用于写入
if (file == NULL) {
perror("Error opening file");
return 1;
}
char ch = 'A';
int result = fputc(ch, file); // 写入字符'A'到文件
if (result == EOF) {
printf("Write error occurred.\n");
} else {
printf("Character '%c' written successfully.\n", ch);
}
fclose(file); // 关闭文件
return 0;
}
5.2 fgetc:从输入流读取一个字符
- 功能:从输入流(如文件或stdin)读取一个字符。读取后,流的位置指示器自动前进。
- 参数 :
stream:指向输入流的FILE*指针(例如文件指针或stdin)。
- 返回值 :
- 成功时:返回读取的字符(
int类型)。 - 文件末尾:返回
EOF(-1),并设置文件结束指示器(可通过feof()检查)。 - 读取错误:返回
EOF,并设置错误指示器(可通过ferror()检查)。
- 成功时:返回读取的字符(
- 重点 :
- 适用于所有输入流。
- 常用于逐字符读取文件,如解析文本。
- 必须检查
feof()和ferror()以区分错误和文件结束。
C语言代码示例:
c
#include <stdio.h>
int main() {
FILE *file = fopen("example.txt", "r"); // 打开文件用于读取
if (file == NULL) {
perror("Error opening file");
return 1;
}
int ch = fgetc(file); // 读取一个字符
if (ch == EOF) {
if (feof(file)) {
printf("End of file reached.\n");
} else if (ferror(file)) {
printf("Read error occurred.\n");
}
} else {
printf("Character read: '%c'\n", (char)ch);
}
fclose(file); // 关闭文件
return 0;
}
5.3 feof 和 ferror:错误检查函数
- feof功能:检测流是否到达文件末尾。
- ferror功能:检测流是否发生读/写错误。
- 参数 :
stream:指向流的FILE*指针。 - 返回值 :
feof:如果文件结束指示器设置,返回非零值;否则返回0。ferror:如果错误指示器设置,返回非零值;否则返回0。
- 重点 :
- 在文件操作后必须使用这些函数检查状态,避免误处理
EOF。 feof仅当读取到文件末尾时返回真,ferror用于捕捉错误。- 通常与
fgetc、fread等函数配合使用。
- 在文件操作后必须使用这些函数检查状态,避免误处理
C语言代码示例:
c
#include <stdio.h>
int main() {
FILE *file = fopen("example.txt", "r");
if (file == NULL) {
perror("Error opening file");
return 1;
}
int ch;
while ((ch = fgetc(file)) != EOF) {
putchar(ch); // 输出字符
}
if (feof(file)) {
printf("\nEnd of file reached.\n");
} else if (ferror(file)) {
printf("\nRead error occurred.\n");
}
fclose(file);
return 0;
}
5.4 fputs:向输出流写入一个字符串
- 功能 :将字符串写入到输出流中(不包括结尾的
\0)。 - 参数 :
str:指向要写入的字符串(必须以\0结尾)。stream:指向输出流的FILE*指针。
- 返回值 :
- 成功时:返回非负整数。
- 失败时:返回
EOF(-1),并设置错误指示器。
- 重点 :
- 适用于文件和stdout。
- 写入时不添加
\0,适合写入整行文本。 - 常用于日志或文本输出。
C语言代码示例:
c
#include <stdio.h>
int main() {
FILE *file = fopen("output.txt", "w");
if (file == NULL) {
perror("Error opening file");
return 1;
}
const char *text = "Hello, World!";
int result = fputs(text, file); // 写入字符串
if (result == EOF) {
printf("Write error occurred.\n");
} else {
printf("String written successfully.\n");
}
fclose(file);
return 0;
}
5.5 fgets:从输入流读取一个字符串
- 功能 :从输入流读取字符串,直到遇到换行符、文件末尾或达到指定字符数(包括
\0)。 - 参数 :
str:指向字符数组的指针,用于存储读取的字符串。num:最大读取字符数(包括\0,实际读取num-1个字符)。stream:指向输入流的FILE*指针。
- 返回值 :
- 成功时:返回
str指针。 - 文件末尾或错误:返回
NULL,需通过feof()或ferror()检查。
- 成功时:返回
- 重点 :
- 读取的字符串包含换行符(如果存在),并以
\0结尾。 - 确保
str指向的数组足够大,防止缓冲区溢出。 - 适用于读取文本行。
- 读取的字符串包含换行符(如果存在),并以
C语言代码示例:
c
#include <stdio.h>
int main() {
FILE *file = fopen("input.txt", "r");
if (file == NULL) {
perror("Error opening file");
return 1;
}
char buffer[100]; // 缓冲区大小
if (fgets(buffer, sizeof(buffer), file) != NULL) {
printf("String read: %s", buffer);
} else {
if (feof(file)) {
printf("End of file reached.\n");
} else if (ferror(file)) {
printf("Read error occurred.\n");
}
}
fclose(file);
return 0;
}
5.6 fprintf:向输出流写入格式化数据
- 功能:将格式化数据写入输出流(如文件或stdout)。
- 参数 :
stream:指向输出流的FILE*指针。format:格式化字符串(如%d,%s)。...:可变参数列表,提供数据。
- 返回值 :
- 成功时:返回写入的字符数(非负值)。
- 失败时:返回负值,并设置错误指示器。
- 重点 :
- 适用于所有输出流,功能类似
printf但更灵活。 - 常用于写入结构化数据,如数字或字符串。
- 需确保格式化字符串与参数匹配。
- 适用于所有输出流,功能类似
C语言代码示例:
c
#include <stdio.h>
int main() {
FILE *file = fopen("data.txt", "w");
if (file == NULL) {
perror("Error opening file");
return 1;
}
int num = 42;
const char *str = "Answer";
int chars_written = fprintf(file, "%s: %d\n", str, num); // 写入格式化数据
if (chars_written < 0) {
printf("Write error occurred.\n");
} else {
printf("%d characters written.\n", chars_written);
}
fclose(file);
return 0;
}
5.7 fscanf:从输入流读取格式化数据
- 功能:从输入流读取格式化数据(如文件或stdin)。
- 参数 :
stream:指向输入流的FILE*指针。format:格式化字符串(如%d,%f)。...:可变参数列表,存储数据的地址。
- 返回值 :
- 成功时:返回成功赋值的参数数量。
- 文件末尾或错误:返回
EOF(-1),需通过feof()或ferror()检查。
- 重点 :
- 适用于所有输入流,功能类似
scanf但更通用。 - 常用于解析文件中的数据。
- 返回值可能少于预期参数数量,需处理匹配失败情况。
- 适用于所有输入流,功能类似
C语言代码示例:
c
#include <stdio.h>
int main() {
FILE *file = fopen("input.txt", "r");
if (file == NULL) {
perror("Error opening file");
return 1;
}
int num;
char str[50];
int items_read = fscanf(file, "%s %d", str, &num); // 读取格式化数据
if (items_read == EOF) {
if (feof(file)) {
printf("End of file reached.\n");
} else if (ferror(file)) {
printf("Read error occurred.\n");
}
} else {
printf("Read %d items: String='%s', Number=%d\n", items_read, str, num);
}
fclose(file);
return 0;
}
5.8 fwrite:向输出流写入数据块
- 功能:将数据块以二进制形式写入文件流。
- 参数 :
ptr:指向要写入的数据块的指针。size:每个数据项的字节大小。count:要写入的数据项数量。stream:指向输出流的FILE*指针。
- 返回值 :返回实际写入的数据项数量(可能小于
count)。 - 重点 :
- 仅适用于文件输出流(需以二进制写入模式打开)。
- 用于高效写入大量数据,如数组或结构体。
- 返回值需检查,确保完整写入。
C语言代码示例:
c
#include <stdio.h>
int main() {
FILE *file = fopen("binary.bin", "wb"); // 二进制写入模式
if (file == NULL) {
perror("Error opening file");
return 1;
}
int data[] = {1, 2, 3, 4, 5};
size_t items_written = fwrite(data, sizeof(int), 5, file); // 写入整个数组
if (items_written != 5) {
printf("Only %zu items written.\n", items_written);
} else {
printf("Data written successfully.\n");
}
fclose(file);
return 0;
}
5.9 fread:从输入流读取数据块
- 功能:从输入流读取数据块到内存缓冲区。
- 参数 :
ptr:指向存储数据的缓冲区的指针。size:每个数据项的字节大小。count:要读取的数据项数量。stream:指向输入流的FILE*指针。
- 返回值 :返回实际读取的数据项数量(可能小于
count)。 - 重点 :
- 仅适用于文件输入流(需以二进制读取模式打开)。
- 用于高效读取二进制数据。
- 缓冲区必须足够大,返回值需检查完整性。
C语言代码示例:
c
#include <stdio.h>
int main() {
FILE *file = fopen("binary.bin", "rb"); // 二进制读取模式
if (file == NULL) {
perror("Error opening file");
return 1;
}
int data[5];
size_t items_read = fread(data, sizeof(int), 5, file); // 读取整个数组
if (items_read != 5) {
printf("Only %zu items read.\n", items_read);
} else {
printf("Data read: ");
for (int i = 0; i < 5; i++) {
printf("%d ", data[i]);
}
printf("\n");
}
fclose(file);
return 0;
}
5.10 对比函数:sprintf 和 sscanf
这些函数用于字符串操作,而非文件流。
- sprintf功能:将格式化数据写入字符串(内存缓冲区)。
- sscanf功能:从字符串解析格式化数据。
- 重点 :
sprintf用于生成字符串,sscanf用于解析字符串。- 与文件函数对比:
fprintf/fscanf针对流,sprintf/sscanf针对字符串。 - 需防止缓冲区溢出(例如,使用
snprintf替代sprintf)。
C语言代码示例:
c
#include <stdio.h>
int main() {
char buffer[100];
int num = 42;
sprintf(buffer, "Number: %d", num); // 写入字符串
printf("Generated string: %s\n", buffer);
int parsed_num;
sscanf(buffer, "Number: %d", &parsed_num); // 解析字符串
printf("Parsed number: %d\n", parsed_num);
return 0;
}
总结
- 关键点 :
- 所有函数都需要错误检查:使用
feof()和ferror()检测文件结束和错误。 - 区分格式化与非格式化函数:格式化函数(如
fprintf)用于结构化数据,非格式化(如fputc)用于原始数据。 - 二进制操作:
fread和fwrite专用于二进制文件,需以"rb"或"wb"模式打开。 - 字符串操作:
sprintf和sscanf处理内存字符串,而非文件流。
- 所有函数都需要错误检查:使用
- 最佳实践 :
- 在文件操作后总是关闭文件(
fclose())。 - 检查返回值以确保操作成功。
- 使用缓冲区时防止溢出(例如,
fgets的num参数)。
- 在文件操作后总是关闭文件(