C语言之文件操作

一、文件的概念

在计算机中,文件 是存储在外部存储介质(硬盘、U 盘等)上的一组相关数据的集合,是操作系统管理数据的基本单位。

从编程视角看,C 语言中的文件分为两类:

  • 文本文件:以字符编码(如 ASCII)存储,可直接用文本编辑器打开查看(如.txt.c文件),数据以 “字符” 为最小单位。

  • 二进制文件:以二进制形式存储(如.exe.jpg.dat文件),无法直接用文本编辑器读懂,数据以 “字节” 为最小单位,存储效率更高、格式更紧凑。

文件操作的本质是程序与外部存储介质之间的数据交互,C 语言通过标准库<stdio.h>提供的文件操作函数实现这一过程。

二、文件路径的概念

要操作文件,首先要明确文件的路径—— 即文件在存储介质中的位置,分为两种形式:

1. 绝对路径

从根目录开始的完整路径,具有唯一性。

  • Windows 示例:D:\code\test.txt(注意:C 语言中需转义反斜杠,写为D:\\code\\test.txt

  • Linux/Mac 示例:/home/user/code/test.txt

2. 相对路径

相对于当前程序运行目录的路径,更灵活,适合项目内文件操作。

  • ./test.txt:当前目录下的test.txt./表示当前目录,可省略)

  • ../data/test.txt:上级目录下data文件夹中的test.txt../表示上级目录)

核心注意点
  • 路径分隔符:Windows 用\(C 代码中需转义为\\),Linux/Mac 用/

  • 相对路径的 “当前目录”:是程序运行的目录,而非代码文件所在目录;

  • 路径错误会导致文件打开失败,需做好错误处理。

三、打开关闭文件

C 语言通过FILE结构体(文件指针)管理文件,核心操作是 “打开 - 操作 - 关闭”,缺一不可(关闭文件可释放资源、确保数据写入磁盘)。

打开文件:fopen()     关闭文件:fclose()
#include <stdio.h>
#include <stdlib.h> 
// 包含exit()

int main() {
    // 函数原型:FILE *fopen(const char *path, const char *mode);
    // 参数1:文件路径;参数2:打开模式;返回值:成功返回文件指针,失败返回NULL
    FILE *fp = fopen("test.txt", "r"); // 以只读模式打开当前目录的test.txt

    // 必须检查打开是否成功
    if (fp == NULL) {
        perror("文件打开失败"); // perror:打印系统错误信息
        exit(EXIT_FAILURE);     // 退出程序,释放资源
    }

    printf("文件打开成功\n");

    // 2. 关闭文件:fclose()
    // 函数原型:int fclose(FILE *stream);
    // 返回值:成功返回0,失败返回EOF(-1)
    if (fclose(fp) != 0) {
        perror("文件关闭失败");
        exit(EXIT_FAILURE);
    }
    printf("文件关闭成功\n");

    return 0;
}
关键说明
  • FILE:是<stdio.h>定义的结构体,存储文件的状态信息(如文件位置、缓冲区等),编程中只需用指针操作,无需关心内部结构;

  • fopen失败原因:文件不存在、权限不足、路径错误等;

  • fclose必须调用:即使程序结束前系统会自动关闭,但主动关闭是良好编程习惯,避免数据丢失(缓冲区数据未刷入磁盘)。

四、读数据

文件读取的核心是 “从文件中读取数据到程序内存”,C 语言提供了 3 类常用函数,适配不同读取场景。

1、fgetc:按字符读取

fgetc从文件中读取单个字符,适合逐字符处理文本文件(如统计字符数、查找字符)。

函数原型:
int fgetc(FILE *stream);

返回值:成功返回读取的字符(ASCII 值),到达文件末尾 / 失败返回EOF(-1)。

代码示例:读取整个文本文件(逐字符)
#include <stdio.h>
#include <stdlib.h>

int main() {
    FILE *fp = fopen("test.txt", "r");
    if (fp == NULL) {
        perror("fopen failed");
        exit(EXIT_FAILURE);
    }

    int ch; // 用int接收(兼容EOF),不能用char(EOF是-1,char可能无法存储)
    // 循环读取,直到文件末尾
    while ((ch = fgetc(fp)) != EOF) {
        putchar(ch); // 输出读取的字符
    }
    printf("\n");

    fclose(fp);
    return 0;
}
核心注意
  • 必须用int接收fgetc的返回值:因为EOF-1,若用char(8 位),当读取到 ASCII 为0xFF的字符时,会被误判为EOF

  • fgetc读取后,文件指针会自动后移 1 个字符,直到文件末尾。

2、fgets:按行读取

fgets从文件中读取一行字符,适合处理文本文件的行数据(如配置文件、日志文件)。

函数原型:
char *fgets(char *str, int n, FILE *stream);
  • 参数 1:存储读取数据的字符数组;

  • 参数 2:读取的最大字符数(n-1个有效字符,最后自动加\0);

  • 参数 3:文件指针;

  • 返回值:成功返回str地址,到达文件末尾 / 失败返回NULL

代码示例:逐行读取文本文件
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main() {
    FILE *fp = fopen("test.txt", "r");
    if (fp == NULL) {
        perror("fopen failed");
        exit(EXIT_FAILURE);
    }

    char buffer[1024]; // 定义缓冲区,存储每行数据
    // 循环逐行读取
    while (fgets(buffer, sizeof(buffer), fp) != NULL) {
        // 去除fgets读取的换行符(可选)
        buffer[strcspn(buffer, "\n")] = '\0';
        printf("读取到行:%s\n", buffer);
    }

    fclose(fp);
    return 0;
}
核心注意
  • fgets会读取换行符\n(如果行长度未超过n-1),需手动去除;

  • 若一行字符数超过n-1fgets会读取前n-1个字符,剩余部分需下次读取;

  • 仅适用于文本文件,二进制文件无 “行” 的概念。

3、fread:按块读取(二进制 / 文本)

fread从文件中读取指定大小的块数据,适合二进制文件(如结构体、数组),也可用于文本文件,效率最高。

函数原型:
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
  • 参数 1:存储数据的内存地址(如数组、结构体指针);

  • 参数 2:单个数据块的大小(字节);

  • 参数 3:要读取的块数;

  • 参数 4:文件指针;

  • 返回值:成功读取的块数(若返回值 < nmemb,可能是文件末尾或出错)。

代码示例 1:读取二进制文件(结构体数据)
#include <stdio.h>
#include <stdlib.h>

// 定义学生结构体
typedef struct {
    char name[20];
    int age;
    float score;
} Student;

int main() {
    FILE *fp = fopen("students.dat", "rb"); // 二进制只读模式:rb
    if (fp == NULL) {
        perror("fopen failed");
        exit(EXIT_FAILURE);
    }

    Student s;
    // 读取1个Student大小的块(sizeof(Student)是单个块大小,1是块数)
    while (fread(&s, sizeof(Student), 1, fp) == 1) {
        printf("姓名:%s,年龄:%d,分数:%.1f\n", s.name, s.age, s.score);
    }

    fclose(fp);
    return 0;
}
代码示例 2:读取文本文件(按块)
#include <stdio.h>
#include <stdlib.h>

int main() {
    FILE *fp = fopen("test.txt", "r");
    if (fp == NULL) {
        perror("fopen failed");
        exit(EXIT_FAILURE);
    }

    char buffer[1024];
    // 每次读取1024字节(1个块,大小1024)
    size_t read_size = fread(buffer, 1, sizeof(buffer), fp);
    if (read_size > 0) {
        buffer[read_size] = '\0'; // 手动加结束符(文本文件)
        printf("读取到%d字节数据:\n%s\n", read_size, buffer);
    }

    fclose(fp);
    return 0;
}
核心注意
  • 二进制文件读取需用rb模式(r是文本模式,Windows 下会转换换行符\n\r\n,导致二进制数据错乱);

  • fread返回的是 “块数”,而非字节数:总读取字节数 = 返回值 × size;

  • 适合批量读取数据,效率远高于逐字符 / 逐行读取。

五、写数据

文件写入的核心是 “将程序内存中的数据写入文件”,对应读取函数,C 语言提供了 3 类写入函数。

1、fputc:按字符写入

fputc向文件中写入单个字符,适合逐字符生成文本文件。

函数原型:
int fputc(int c, FILE *stream);
  • 参数 1:要写入的字符(ASCII 值);

  • 参数 2:文件指针;

  • 返回值:成功返回写入的字符,失败返回EOF

代码示例:写入字符串到文件(逐字符)
#include <stdio.h>
#include <stdlib.h>

int main() {
    // 以写入模式打开(不存在则创建,存在则清空)
    FILE *fp = fopen("output.txt", "w");
    if (fp == NULL) {
        perror("fopen failed");
        exit(EXIT_FAILURE);
    }

    char *str = "Hello, File Operation!";
    int i = 0;
    // 逐字符写入,直到字符串结束符
    while (str[i] != '\0') {
        fputc(str[i], fp);
        i++;
    }

    fclose(fp);
    printf("字符写入完成\n");
    return 0;
}
2、fputs:按行写入

fputs向文件中写入一行字符串,适合批量写入文本行。

函数原型:
int fputs(const char *str, FILE *stream);
  • 参数 1:要写入的字符串(不含\0,可手动加\n换行);

  • 参数 2:文件指针;

  • 返回值:成功返回非负数,失败返回EOF。

代码示例:逐行写入多行文本
#include <stdio.h>
#include <stdlib.h>

int main() {
    // 以追加模式打开(不存在则创建,存在则在末尾追加)
    FILE *fp = fopen("output.txt", "a");
    if (fp == NULL) {
        perror("fopen failed");
        exit(EXIT_FAILURE);
    }

    char *lines[] = {"第一行数据\n", "第二行数据\n", "第三行数据\n"};
    int n = sizeof(lines) / sizeof(lines[0]);

    for (int i = 0; i < n; i++) {
        fputs(lines[i], fp);
    }

    fclose(fp);
    printf("行写入完成\n");
    return 0;
}
核心注意
  • fputs不会自动添加换行符,需手动写入\n

  • 追加模式a:写入的数据会追加到文件末尾,不会清空原有内容。

3、fwrite:按块写入(二进制 / 文本)

fwrite向文件中写入指定大小的块数据,适合二进制文件(如结构体、数组),效率最高。

函数原型
size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);
  • 参数 1:要写入的数据地址;

  • 参数 2:单个数据块的大小(字节);

  • 参数 3:要写入的块数;

  • 参数 4:文件指针;

  • 返回值:成功写入的块数(若返回值 < nmemb,说明写入失败)。

代码示例:写入结构体数据到二进制文件
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef struct {
    char name[20];
    int age;
    float score;
} Student;

int main() {
    // 二进制写入模式:wb(不存在则创建,存在则清空)
    FILE *fp = fopen("students.dat", "wb");
    if (fp == NULL) {
        perror("fopen failed");
        exit(EXIT_FAILURE);
    }

    // 定义要写入的学生数据
    Student students[] = {
        {"张三", 18, 90.5},
        {"李四", 19, 88.0},
        {"王五", 20, 95.5}
    };
    int n = sizeof(students) / sizeof(students[0]);

    // 写入n个Student块
    size_t write_num = fwrite(students, sizeof(Student), n, fp);
    if (write_num != n) {
        perror("fwrite failed");
        fclose(fp);
        exit(EXIT_FAILURE);
    }

    fclose(fp);
    printf("成功写入%d个学生数据\n", write_num);
    return 0;
}
核心注意
  • 二进制文件写入需用wb模式(避免文本模式的换行符转换);

  • fwrite可写入任意类型数据(数组、结构体、基本类型),是二进制文件操作的首选;

  • 写入后建议调用fflush(fp)强制刷新缓冲区(确保数据立即写入磁盘,避免程序崩溃导致数据丢失)。

六、文件读写模式

fopen的第二个参数(模式)决定了文件的操作方式,是文件操作的核心,需根据场景精准选择。

常用模式对照表
模式含义适用文件类型核心特点
r只读文本文件文件必须存在,否则打开失败
w只写文本文件不存在则创建,存在则清空内容
a追加文本文件不存在则创建,写入内容追加到末尾
r+读写文本文件文件必须存在,可读写,从开头开始
w+读写文本文件不存在则创建,存在则清空,可读写
a+读写文本文件不存在则创建,写入追加到末尾,可读
rb只读二进制文件r,但不转换换行符
wb只写二进制文件w,但不转换换行符
ab追加二进制文件a,但不转换换行符
rb+/r+b读写二进制文件r+,二进制模式
wb+/w+b读写二进制文件w+,二进制模式
ab+/a+b读写二进制文件a+,二进制模式
关键说明
  1. 文本模式 vs 二进制模式

    • 文本模式(无b):Windows 下会将\n转换为\r\n(换行符),Linux/Mac 无转换;

    • 二进制模式(有b):不做任何转换,原样读写,适合图片、视频、结构体等二进制数据。

  2. 追加模式a的特殊性

    • 无论文件指针位置如何,写入的数据始终追加到文件末尾;

    • a+模式下,读取可从任意位置,但写入仍追加到末尾。

  3. 读写模式(如r+

    • 需注意文件指针位置,避免读写覆盖;

    • 读写切换时,需用fflushfseekfsetposrewind刷新 / 重置指针。

代码示例:文件指针移动(fseek)+ 读写模式
#include <stdio.h>
#include <stdlib.h>

int main() {
    FILE *fp = fopen("test.txt", "w+"); // 读写模式,创建并清空文件
    if (fp == NULL) {
        perror("fopen failed");
        exit(EXIT_FAILURE);
    }

    // 写入数据
    fputs("Hello, C File!", fp);

    // 移动文件指针到开头(读写切换需重置指针)
    fseek(fp, 0, SEEK_SET); // SEEK_SET:文件开头;SEEK_CUR:当前位置;SEEK_END:文件末尾

    // 读取数据
    char buffer[1024];
    fgets(buffer, sizeof(buffer), fp);
    printf("读取到数据:%s\n", buffer);

    fclose(fp);
    return 0;
}

总结

本文系统讲解了 C 语言文件操作的全流程,核心要点如下:

  1. 基础核心:文件操作遵循 “打开 - 操作 - 关闭” 流程,fopen返回的FILE*是操作入口,必须检查打开 / 关闭是否成功;

  2. 读写选择:文本文件优先用fgetc/fputs(逐字符 / 行),二进制文件优先用fread/fwrite(按块,效率最高);

  3. 模式关键:区分文本模式(无b)和二进制模式(有b),w清空、a追加、r只读是最常用的基础模式。

以上便是我对于C语言中的文件管理的见解,欢迎各位一起探讨。

源码链接: https://pan.quark.cn/s/7b9e1590db2e 在本计划中,我们聚焦于一个基于数字逻辑的药片装瓶系统的构建,这构成了北京邮电大学(北邮)在小学期内向学生提供的一次课程设计课题。该系统致力于模拟实际药品包装的操作流程,借助电子操控和自动化技术达成药片的高效且精准的装瓶目标。以下是对该系统设计所涉及的关键知识领域的详尽阐述: 1. **数字逻辑**:数字逻辑是电子工程领域的核心学科,主要探究如何运用二进制数字进行信息的表征与处理。在此项目中,数字逻辑用于构建和实现系统的控制机制,诸如计数器、编码器、解码器、触发器等,旨在保障药片装瓶过程的精确调控。 2. **硬件电路构建**:系统可能整合微控制器、传感器、执行机构等硬件单元。例如,微控制器作为系统的心脏,负责接收输入信号,处理数据,并指挥执行机构执行药片装填。传感器负责监测药片的数量和瓶装进度,而执行机构如电机则负责实际完成装瓶动作。 3. **计数器**:在药片装瓶的操作过程中,计数器用于追踪已装入瓶子的药片总数,确保达到预设的剂量标准。这可能需要设计同步计数器或异步计数器,以实现精确计数并触发装瓶操作。 4. **编码与解码**:编码器将特定的信息(例如药片种类或剂量)转化为二进制编码,便于硬件设备进行处理;解码器则将这些编码解读为可执行的操作,如切换装瓶路径或启动封盖流程。 5. **触发器**:在系统中,触发器可用于在特定条件达成时启动或中止某个操作,例如当瓶子达到满载时关闭装填机制。 6. **传感器技术**:可能包含重量传感器、光电传感器或机械触碰开关,用于识别瓶子的存在、位置以及药片的数量。这些传感器的精确度直接关联到整个系统的性能水平。 7. **控制算法**...
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值