新手入门指南:认识 C 语言文件操作(上)

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 表示相对路径。相对路径便于在脚本或程序中灵活引用文件。
  • 文件名主干 :文件的主要名称,由用户自定义(如 reportreport.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;
}

代码逐步解释

  1. 变量定义

    • int a = 10000;:这行代码定义了一个整数变量a,并赋值为10000。在内存中,a以二进制形式存储。例如,假设int类型占4字节(32位系统),10000的二进制表示为10011100010000210011100010000_2100111000100002(实际存储为小端字节序)。
  2. 打开文件

    • FILE* pf = fopen("test.txt", "wb");
      • fopen函数用于打开文件。第一个参数是文件名("test.txt"),第二个参数是模式字符串。
      • 模式"wb"表示以二进制写入(write binary)方式打开文件。如果文件不存在,则创建新文件;如果存在,则覆盖原有内容。
      • FILE* pf声明一个文件指针pf,用于后续操作文件。
  3. 写入数据

    • fwrite(&a, 4, 1, pf);
      • fwrite函数用于以二进制形式写入数据到文件。参数解释如下:
        • 第一个参数&a:这是数据源的地址。&a表示变量a的内存地址(指针),指向存储10000的二进制数据。
        • 第二个参数4:表示每个数据元素的大小(字节数)。这里设置为4,因为int类型通常占4字节(取决于系统)。
        • 第三个参数1:表示要写入的元素数量。这里设置为1,因为只写入一个整数。
        • 第四个参数pf:文件指针,指定写入的目标文件。
      • 这行代码的作用是:直接将内存中a的二进制内容(4字节)写入文件,不加任何转换。因此,文件"test.txt"的内容将是二进制序列,而不是可读的字符。
  4. 关闭文件和处理指针

    • 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")和写入函数(如fwrite vs fprintf)至关重要。

4. 详细解释:C语言中的文件打开与关闭

4.1 流和标准流

在C语言中,流(Stream) 是数据输入/输出的抽象概念,它将物理设备(如键盘、显示器)或文件抽象为连续的字节序列。

  • 标准流(Standard Streams):C程序启动时自动打开三个预定义流:

    • stdin(标准输入流) :通常关联键盘输入。函数如 scanfstdin 读取数据。
    • stdout(标准输出流) :通常关联显示器输出。函数如 printfstdout 写入数据。
    • stderr(标准错误流):专用于错误消息输出,也关联显示器,确保错误信息不被重定向干扰。

    这三个流的类型均为 FILE*(文件指针),可直接用于输入/输出操作。例如:

    c 复制代码
    int num;
    scanf("%d", &num);       // 从 stdin 读取输入
    printf("Value: %d", num); // 向 stdout 输出
    perror("Error");         // 向 stderr 输出错误

4.2 文件指针

文件操作的核心是 文件指针(File Pointer) ,类型为 FILE*FILE 是一个系统定义的结构体(在 <stdio.h> 中声明),存储文件的元信息(如文件名、状态、当前位置等)。每个打开的文件在内存中有一个对应的 FILE 结构体实例。

  • 作用 :文件指针(如 FILE* pf;)指向这个结构体,通过它间接访问文件。例如:

    c 复制代码
    FILE* pf; // 声明文件指针变量

    当文件打开后,pf 指向该文件的 FILE 结构体,所有文件操作(读/写)都通过 pf 进行。

4.3 文件的打开与关闭

文件操作遵循"打开 → 读写 → 关闭"的流程:

  • 打开文件 :使用 fopen 函数。

    c 复制代码
    FILE *fopen(const char *filename, const char *mode);
    • 参数
      • filename:文件路径(绝对或相对路径,如 "test.txt")。
      • mode:操作模式(如 "r" 只读),详见下文表格。
    • 返回值
      • 成功:返回指向 FILE 结构的指针。
      • 失败:返回 NULL(必须检查,否则可能导致程序崩溃)。
    • 关键点fopen 将文件与流关联,后续操作通过返回的指针进行。
  • 关闭文件 :使用 fclose 函数。

    c 复制代码
    int 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)详解

fopenmode 参数定义了文件的访问方式。以下是基于C语言代码的解释,展示不同模式的行为。

代码示例解释不同模式
  • "r"(只读文本文件)

    文件必须存在,否则失败。

    c 复制代码
    FILE* file = fopen("data.txt", "r"); // 只读打开
    if (file == NULL) {
        perror("Failed to open"); // 文件不存在时输出错误
    }
  • "w"(只写文本文件)

    文件不存在则创建;存在则清空内容。

    c 复制代码
    FILE* file = fopen("output.txt", "w"); // 只写打开
    if (file != NULL) {
        fprintf(file, "New content"); // 写入数据(覆盖原有内容)
    }
  • "a"(追加文本文件)

    文件不存在则创建;存在则在末尾添加数据。

    c 复制代码
    FILE* file = fopen("log.txt", "a"); // 追加模式
    if (file != NULL) {
        fprintf(file, "New log entry\n"); // 在文件尾添加
    }
  • "rb"(只读二进制文件)

    类似 "r",但用于二进制文件(如图片)。

    c 复制代码
    FILE* file = fopen("image.bin", "rb"); // 二进制只读
  • "r+"(读写文本文件)

    文件必须存在,支持读取和写入。

    c 复制代码
    FILE* file = fopen("data.txt", "r+"); // 读写模式
    if (file != NULL) {
        char buffer[100];
        fscanf(file, "%s", buffer); // 读取
        fseek(file, 0, SEEK_SET);   // 移动指针到开头
        fprintf(file, "Updated");   // 写入
    }
  • "w+"(读写文本文件)

    文件不存在则创建;存在则清空。支持读写。

    c 复制代码
    FILE* 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用于捕捉错误。
    • 通常与fgetcfread等函数配合使用。

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)用于原始数据。
    • 二进制操作:freadfwrite专用于二进制文件,需以"rb""wb"模式打开。
    • 字符串操作:sprintfsscanf处理内存字符串,而非文件流。
  • 最佳实践
    • 在文件操作后总是关闭文件(fclose())。
    • 检查返回值以确保操作成功。
    • 使用缓冲区时防止溢出(例如,fgetsnum参数)。
相关推荐
cany10001 小时前
C++ -- 动态内存分配和释放(new/delete)
开发语言·c++
暴躁小师兄数据学院1 小时前
【AI大数据工程师特训笔记】第08讲:集合运算与超级函数
大数据·笔记·sql·ai·postgresql
brycegao3212 小时前
Vue3+Go 全栈项目上线阿里云|从 0 到 1 踩坑全纪录
开发语言·阿里云·golang
ch.ju2 小时前
Java Programming Chapter 4——cite
java·开发语言
searchforAI2 小时前
我的Obsidian知识库,现在可以自动剪藏笔记到本地了
人工智能·笔记·学习·音视频·ai工具·obsidian·视频总结
优雅格子衫2 小时前
uniapp 拍照相册选取后超级好用的裁剪组件,增加水印完全自定义
开发语言·前端·javascript·uni-app·vue
Vallelonga2 小时前
Rust 中 unsafe 关键字的语义
开发语言·rust
AI砖家2 小时前
前端 JavaScript 异步处理全方案详解:从回调到 Observable
开发语言·前端·javascript
社交怪人2 小时前
【A×B】信息学奥赛一本通C语言解法(题号1036)
c语言