云计算百科
云计算领域专业知识百科平台

C语言文件操作

在C语言开发中,文件操作是实现数据持久化的核心手段,能让程序数据脱离内存限制、长期存储在硬盘中。本文将从文件基础概念、指针操作、顺序/随机读写,到缓冲区、错误判断,全方位拆解C语言文件操作的核心知识点,附完整代码示例与避坑指南,零基础也能快速掌握。


一、文件操作的核心意义

程序运行时,所有数据默认存储在内存中,程序退出/电脑关机后内存数据会完全丢失;
文件操作的核心目的是实现数据持久化,将数据保存到硬盘的文件中,实现数据的长期存储和跨程序使用。

二、文件的分类(程序设计视角)

在C语言程序开发中,文件分为程序文件和数据文件两类,核心操作对象为数据文件。

2.1 程序文件

用于构成程序的文件,仅被编译器/操作系统解析,程序运行时一般不主动读写:

• 源文件:后缀.c,编写的C语言代码文件

• 目标文件:后缀.obj,源文件编译后生成的二进制文件(Windows系统)

• 可执行文件:后缀.exe,目标文件链接后生成的可运行文件(Windows系统)

2.2 数据文件

程序运行时主动读写的文件,用于存储程序的输入/输出数据,比如配置文件、日志文件、用户数据文件等。
文件名规范:一个完整的文件名由3部分组成,文件路径 + 文件名主干 + 文件后缀,示例:

• 绝对路径:D:\\C_Code\\project\\test.txt

• 相对路径:./data/info.dat(./表示当前程序所在目录)

三、文本文件 vs 二进制文件(数据存储形式)

根据数据在文件中的存储格式,数据文件分为文本文件和二进制文件,核心区别是是否按ASCII码转换存储。

3.1 文本文件

• 数据以ASCII字符的形式存储,可被记事本、VS Code等文本编辑器直接打开查看

• 存储前需将内存中的二进制数据转换为ASCII码,存在转换开销

• 示例:整数10000(内存中4字节二进制),会被拆分为1、0、0、0、05个ASCII字符,占5字节

3.2 二进制文件

• 数据直接按内存中的二进制形式存储,无需转换,无格式开销

• 无法被文本编辑器直接识别(打开为乱码),需对应程序解析

• 示例:整数10000直接按4字节二进制存储,占用空间更小

3.3 核心对比

特性 文本文件 二进制文件
存储格式 ASCII字符 内存原二进制
可读性 高(可直接打开) 低(乱码)
空间占用 较大 较小
读写效率 较低(需转换) 较高(无转换)
适用场景 配置文件、日志文件等 数据备份、大型数据存储等 

四、文件指针(FILE*)—— 文件操作的核心句柄

4.1 文件信息区

系统为每个打开的文件,在内存中自动创建一个结构体变量,称为文件信息区。
该结构体中存储了文件的核心信息:文件名、文件打开模式、文件当前读写位置、文件缓冲区地址、文件状态等。
C语言标准库中规定了该结构体的通用名FILE(不同编译器的FILE结构体内部实现略有差异,但核心成员一致)。

4.2 文件指针的定义与作用

• 定义:FILE* 指针名;(如FILE* pf;),FILE*是指向FILE结构体的指针类型

• 作用:通过文件指针指向文件的信息区,程序对文件的所有操作,都通过操作该指针完成(无需直接操作FILE结构体)

• 注意:文件指针仅在文件打开后有效,文件关闭后失效,需及时置NULL避免野指针。

4.3 标准流文件指针(程序启动默认打开)

C语言程序启动时,系统会自动打开3个标准流文件,对应3个默认的文件指针,可直接使用:

标准流指针 对应设备 作用
stdin 键盘 标准输入流
stdout 屏幕 标准输出流
stderr 屏幕 标准错误输出流 

示例:fputc('a', stdout); 等价于printf("a");,直接在屏幕输出字符a。   

五、文件的打开(fopen)与关闭(fclose)—— 必成对使用

文件操作的基本流程:打开文件(fopen) → 读写文件 → 关闭文件(fclose),打开和关闭必须成对出现,否则会导致文件泄漏、缓冲区数据丢失、文件被占用等问题。

5.1 打开文件:fopen函数

函数原型

#include <stdio.h>  // 头文件必须包含
FILE *fopen(const char *filename, const char *mode);

参数解析

• filename:字符串类型,传入文件路径+文件名(绝对路径/相对路径)

• mode:字符串类型,传入文件打开模式,决定文件的读写权限、操作类型

返回值

• 成功:返回指向该文件信息区的FILE*指针(文件指针)

• 失败:返回NULL(如文件不存在、权限不足、路径错误),并设置错误码

必做操作:判空

由于fopen可能失败,打开文件后必须判断返回值是否为NULL,避免后续操作野指针:

// 示例:以只读模式打开test.txt
FILE* pf = fopen("test.txt", "r");
if (pf == NULL)  // 判空:失败则打印错误信息并退出
{
    perror("fopen fail");  // perror:打印错误原因(需包含stdio.h)
    return 1;  // 非0退出,标识程序异常
}
// 成功则继续后续读写操作

5.2 核心:文件打开模式(mode)

mode是字符串常量(必须加双引号),不同模式对应不同的文件操作权限,最常用r/w/a/rb/wb,其余为扩展模式,核心模式详解如下(分文本/二进制):

文本文件打开模式(核心)

模式 中文描述 核心权限 文件不存在时 文件存在时 初始读写位置
"r" 只读打开 仅读,不可写 打开失败 保留原有内容 文件开头
"w" 只写打开 仅写,不可读 创建新文件 清空原有内容 文件开头
"a" 追加写入 仅写,不可读 创建新文件 保留原有内容 文件末尾
"r+" 读写打开 可读可写 打开失败 保留原有内容 文件开头
"w+" 读写创建 可读可写 创建新文件 清空原有内容 文件开头
"a+" 读写追加 可读可写 创建新文件 保留原有内容 文件末尾

二进制文件打开模式(核心)

在文本模式后加b(binary),表示按二进制格式操作,无ASCII转换,核心模式:

模式 中文描述 核心权限 文件不存在时 文件存在时
"rb" 只读打开二进制 仅读,二进制 打开失败 保留原有内容 
"wb" 只写打开二进制 仅写,二进制 创建新文件 清空原有内容 
"ab" 追加写入二进制 仅写,二进制 创建新文件 保留原有内容 

模式使用注意

1. "r"是最安全的读模式,不会修改文件,文件不存在时直接失败,避免误创建;

2. "w"模式慎用,文件存在时会直接清空内容,无法恢复;

3. "a"模式是追加写,所有写入操作都在文件末尾,不会覆盖原有内容,适合写日志;

4. 二进制模式的b必须小写,部分编译器(如VC++)不支持大写B。

5.3 关闭文件:fclose函数

函数原型

#include <stdio.h>
int fclose(FILE *stream);

参数解析

• stream:传入需要关闭的文件指针(fopen成功返回的指针)

返回值

• 成功:返回0

• 失败:返回EOF(EOF是C语言常量,值为-1,定义在stdio.h中)

必做操作:关闭后置NULL

文件关闭后,系统会释放该文件的信息区,原文件指针变为野指针,必须手动置NULL:

// 示例:关闭文件指针pf
if (fclose(pf) != 0)  // 可选:判断关闭是否成功
{
    perror("fclose fail");
    return 1;
}
pf = NULL;  // 核心:置NULL,避免野指针

5.4 打开+关闭完整代码示例

#include <stdio.h>
int main()
{
    // 1. 以只读模式打开文本文件test.txt
    FILE* pf = fopen("test.txt", "r");
    // 2. 判空:打开失败则退出
    if (pf == NULL)
    {
        perror("fopen: test.txt");
        return 1;
    }
    // 3. 此处编写文件读写代码(暂时省略)
    
    // 4. 关闭文件
    fclose(pf);
    // 5. 置NULL,避免野指针
    pf = NULL;

    return 0;
}

六、文件的顺序读写——按顺序从开头/末尾读写

顺序读写是指:从文件的初始位置开始,按字节/字符/行的顺序,依次读写,读写位置自动后移,核心对应6组函数,按操作单位分类,附完整代码示例+解析。

前置说明

1. 所有读写函数都需包含<stdio.h>头文件;

2. 函数参数中的stream均为文件指针(fopen成功的指针);

3. 读写失败/到达文件末尾时,多数函数返回EOF或NULL;

4. 以下示例均基于“文件打开成功”,省略判空代码(实际开发必须加)。

6.1 字符级读写:fputc(写)+ fgetc(读)—— 单字符操作

1. 写字符:fputc

• 原型:int fputc(int ch, FILE *stream);

• 作用:将单个字符写入指定文件

• 参数:ch为要写入的字符(int型是为了兼容EOF,实际传char即可);stream为文件指针

• 返回值:成功返回写入的字符(ASCII值);失败返回EOF

• 示例:向test.txt写入字符a、b、c

FILE* pf = fopen("test.txt", "w");  // 只写模式打开
fputc('a', pf);
fputc('b', pf);
fputc('c', pf);  // 写入后文件内容:abc
fclose(pf);
pf = NULL;

2. 读字符:fgetc

• 原型:int fgetc(FILE *stream);

• 作用:从指定文件读取单个字符,读取后文件指针自动后移1字节

• 参数:stream为文件指针

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

• 示例:读取test.txt中的字符(内容abc)

FILE* pf = fopen("test.txt", "r");  // 只读模式打开
int ch = 0;
// 循环读取:直到fgetc返回EOF(末尾)
while ((ch = fgetc(pf)) != EOF)
{
    printf("%c ", ch);  // 输出:a b c
}
fclose(pf);
pf = NULL;

• 关键:fgetc执行一次,指针后移一次,再次执行读取下一个字符,直到末尾返回EOF。

6.2 字符串级读写:fputs(写)+ fgets(读)—— 整行/整串操作

1. 写字符串:fputs

• 原型:int fputs(const char *str, FILE *stream);

• 作用:将一个字符串写入指定文件(不会自动加换行符,需手动加\\n)

• 参数:str为要写入的字符串;stream为文件指针

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

• 示例:向test.txt写入字符串hello和world(换行)

FILE* pf = fopen("test.txt", "w");
fputs("hello\\n", pf);  // 手动加\\n,实现换行
fputs("world", pf);    // 文件内容:hello\\nworld
fclose(pf);
pf = NULL;

2. 读字符串:fgets

• 原型:char *fgets(char *str, int num, FILE *stream);

• 作用:从指定文件读取最多num-1个字符,存入str数组,自动在末尾加'\\0' 构成字符串

• 停止读取的3种情况:① 读取了num-1个字符;② 读到换行符\\n;③ 到达文件末尾

• 参数:str为存储字符串的数组;num为读取的最大字符数;stream为文件指针

• 返回值:成功返回str数组的首地址;到达末尾/失败返回NULL

• 示例:读取test.txt中的字符串(内容hello\\nworld,数组大小10)

FILE* pf = fopen("test.txt", "r");
char buf[10] = {0};  // 定义缓冲区存储读取的字符串
// 循环读取:直到fgets返回NULL
while (fgets(buf, 10, pf) != NULL)
{
    printf("%s", buf);  // 输出:hello 换行 world
}
fclose(pf);
pf = NULL;

• 关键:fgets会保留换行符\\n,是区别于gets(已废弃)的核心点,避免缓冲区溢出。

6.3 格式化读写:fprintf(写)+ fscanf(读)—— 按格式操作(类似printf/scanf)

1. 格式化写:fprintf

• 原型:int fprintf(FILE *stream, const char *format, …);

• 作用:按指定格式将数据写入文件(比printf多了第一个参数stream,其余完全一致)

• 适用场景:写入结构体、整数、浮点数等复合数据,无需手动转换格式

• 示例:将结构体数据写入文件

// 定义结构体
struct Student
{
    char name[20];
    int age;
    float score;
};
int main()
{
    struct Student s = {"张三", 18, 95.5};
    FILE* pf = fopen("student.txt", "w");
    // 格式化写入:格式符与printf一致
    fprintf(pf, "姓名:%s 年龄:%d 成绩:%.1f", s.name, s.age, s.score);
    fclose(pf);
    pf = NULL;
    return 0;
}
// 文件内容:姓名:张三 年龄:18 成绩:95.5

2. 格式化读:fscanf

• 原型:int fscanf(FILE *stream, const char *format, …);

• 作用:从文件按指定格式读取数据(比scanf多了第一个参数stream,其余完全一致)

• 适用场景:读取文件中的格式化数据,直接存入变量/结构体

• 示例:从student.txt读取数据到结构体

struct Student
{
    char name[20];
    int age;
    float score;
};
int main()
{
    struct Student s = {0};
    FILE* pf = fopen("student.txt", "r");
    // 格式化读取:格式符与写入时一致
    fscanf(pf, "姓名:%s 年龄:%d 成绩:%f", s.name, &s.age, &s.score);
    // 打印验证:输出姓名:张三 年龄:18 成绩:95.500000
    printf("姓名:%s 年龄:%d 成绩:%f", s.name, s.age, s.score);
    fclose(pf);
    pf = NULL;
    return 0;
}

• 关键:读取格式必须与写入格式完全一致,否则读取数据错误。

6.4 二进制读写:fwrite(写)+ fread(读)—— 二进制块操作(高效)

核心特点

• 按内存二进制格式读写,无ASCII转换,效率最高,占用空间最小;

• 直接操作数据块(如整个结构体、整个数组),适合存储复合数据;

• 写入的文件为二进制文件,文本编辑器打开为乱码,需对应程序解析。

1. 二进制写:fwrite

• 原型:size_t fwrite(const void *ptr, size_t size, size_t count, FILE *stream);

• 核心参数解析(size_t是无符号整数,定义在stdio.h):

◦ ptr:要写入的数据首地址(void*兼容所有类型);

◦ size:单个数据的字节大小(如int为4,结构体为sizeof(结构体));

◦ count:要写入的数据个数;

◦ stream:文件指针。

• 返回值:成功返回实际写入的数据个数;失败返回小于count的数(或0)。

• 示例:将结构体数组以二进制格式写入student.dat

struct Student
{
    char name[20];
    int age;
    float score;
};
int main()
{
    // 定义结构体数组并初始化
    struct Student s[2] = {{"张三", 18, 95.5}, {"李四", 19, 90.0}};
    // 二进制只写模式打开(wb)
    FILE* pf = fopen("student.dat", "wb");
    // 写入:单个数据大小sizeof(struct Student),个数2
    fwrite(s, sizeof(struct Student), 2, pf);
    fclose(pf);
    pf = NULL;
    return 0;
}

2. 二进制读:fread

• 原型:size_t fread(void *ptr, size_t size, size_t count, FILE *stream);

• 作用:从二进制文件读取count个size字节的数据,存入ptr指向的内存空间

• 参数:与fwrite完全一致(ptr为存储数据的内存首地址)

• 返回值:成功返回实际读取的数据个数;到达文件末尾/失败返回小于count的数(或0)

• 示例:从student.dat读取二进制数据到结构体数组

struct Student
{
    char name[20];
    int age;
    float score;
};
int main()
{
    struct Student s[2] = {0};  // 初始化数组为0
    // 二进制只读模式打开(rb)
    FILE* pf = fopen("student.dat", "rb");
    // 读取:单个数据大小sizeof(struct Student),个数2
    size_t ret = fread(s, sizeof(struct Student), 2, pf);
    // 打印读取结果
    for (int i = 0; i < ret; i++)
    {
        printf("姓名:%s 年龄:%d 成绩:%.1f\\n", s[i].name, s[i].age, s[i].score);
    }
    fclose(pf);
    pf = NULL;
    return 0;
}
// 输出:
// 姓名:张三 年龄:18 成绩:95.5
// 姓名:李四 年龄:19 成绩:90.0

• 关键:fread/fwrite的size和count必须与写入时一致,否则数据读取错乱。

6.5 顺序读写函数对比表(核心)

函数 操作类型 操作单位 适用文件 核心特点
fputc 单个字符 文本文件 逐字符写入,无自动换行
fgetc 单个字符 文本文件 逐字符读取,指针自动后移 
fputs 字符串 文本文件 逐串写入,需手动加\\n 
fgets 字符串 文本文件 最多读num-1个,保留\\n,加\\0 
fprintf 格式化数据 文本文件 兼容所有数据类型,格式灵活 
fscanf 格式化数据 文本文件 按格式读取,需与写入格式一致 
fwrite 二进制块 二进制文件 无转换,高效,适合复合数据 
fread 二进制块 二进制文件 无转换,高效,参数需严格匹配 

七、文件的随机读写——任意位置读写(核心3函数)

顺序读写只能从开头/末尾依次操作,而随机读写可以通过移动文件指针,直接跳转到文件的任意位置进行读写,核心依赖3个函数:fseek(定位指针)、ftell(获取指针位置)、rewind(重置指针)。

7.1 核心前提:文件指针的“当前位置”

文件指针的当前读写位置是随机读写的核心,默认规则:

• "r"/"w"模式:初始位置为文件开头(偏移量0);

• "a"模式:初始位置为文件末尾;

• 顺序读写时,指针会自动后移(如fgetc后移1字节,fgets后移n字节);

• 随机读写的本质:手动修改指针的当前位置。

7.2 定位文件指针:fseek(核心)

函数原型

int fseek(FILE *stream, long int offset, int origin);

核心参数解析

• stream:文件指针

• offset:偏移量(long int型),单位为字节;正数表示向后偏移,负数表示向前偏移

• origin:偏移起始位置,C语言标准库定义了3个常量,必须使用:

常量 对应位置 含义
SEEK_SET 0 文件开头
SEEK_CUR 1 文件指针当前位置 
SEEK_END 2 文件末尾

返回值

• 成功:返回0

• 失败:返回非0值

示例:文件内容为abcdef(6个字符,偏移量0-5),通过fseek定位并读取

FILE* pf = fopen("test.txt", "r");
int ch = 0;
// 1. 从文件开头(SEEK_SET)向后偏移3字节,定位到d(偏移量3)
fseek(pf, 3, SEEK_SET);
ch = fgetc(pf);  // 读取d
printf("%c\\n", ch);  // 输出d

// 2. 从当前位置(d,偏移3)向前偏移2字节,定位到b(偏移1)
fseek(pf, -2, SEEK_CUR);
ch = fgetc(pf);  // 读取b
printf("%c\\n", ch);  // 输出b

// 3. 从文件末尾(SEEK_END)向前偏移2字节,定位到e(偏移4)
fseek(pf, -2, SEEK_END);
ch = fgetc(pf);  // 读取e
printf("%c\\n", ch);  // 输出e

fclose(pf);
pf = NULL;

7.3 获取当前指针偏移量:ftell

函数原型

long int ftell(FILE *stream);

作用

返回文件指针当前位置相对于文件开头的字节偏移量(long int型)

返回值

• 成功:返回偏移量(非负整数)

• 失败:返回-1L(long型的-1)

示例:获取上述示例中指针的偏移量

FILE* pf = fopen("test.txt", "r");
fseek(pf, 3, SEEK_SET);
long pos = ftell(pf);
printf("%ld\\n", pos);  // 输出3(当前偏移量3)
fclose(pf);
pf = NULL;

7.4 重置文件指针到开头:rewind

函数原型

void rewind(FILE *stream);

作用

1. 将文件指针重置到文件开头(偏移量0);

2. 同时清除文件的错误标志和文件结束标志(feof/ferror的标志)。

无返回值,直接使用

示例:读取后通过rewind重置指针,重新读取

FILE* pf = fopen("test.txt", "r");
int ch = fgetc(pf);  // 读取a,指针偏移1
printf("%c\\n", ch);  // 输出a
rewind(pf);          // 重置指针到开头(偏移0)
ch = fgetc(pf);      // 再次读取a
printf("%c\\n", ch);  // 输出a
fclose(pf);
pf = NULL;

7.5 随机读写完整示例

#include <stdio.h>
int main()
{
    FILE* pf = fopen("test.txt", "w");
    fputs("abcdef", pf);  // 写入abcdef,偏移0-5
    fclose(pf);

    pf = fopen("test.txt", "r");
    int ch = 0;
    // 1. 定位到偏移3(d)
    fseek(pf, 3, SEEK_SET);
    ch = fgetc(pf);
    printf("偏移3:%c\\n", ch);  // d
    // 2. 获取当前偏移量
    printf("当前偏移:%ld\\n", ftell(pf));  // 4
    // 3. 重置到开头
    rewind(pf);
    printf("重置后偏移:%ld\\n", ftell(pf));  // 0
    // 4. 从末尾向前偏移2(e)
    fseek(pf, -2, SEEK_END);
    ch = fgetc(pf);
    printf("末尾前2:%c\\n", ch);  // e

    fclose(pf);
    pf = NULL;
    return 0;
}

八、文件结束与错误判断——feof + ferror(必用)

8.1 核心误区:feof的作用

feof不是用来判断文件是否读取结束的,而是用来判断“文件读取结束的原因”:

• 读取结束的两种可能:① 正常到达文件末尾(EOF);② 读写过程中发生错误(如文件损坏、权限丢失);

• feof的作用:当读取结束后,判断是否是正常到达末尾;

• ferror的作用:当读取结束后,判断是否是发生错误导致结束。

8.2 核心函数:feof + ferror

1. feof函数

• 原型:int feof(FILE *stream);

• 作用:判断文件是否正常到达末尾

• 返回值:是(正常末尾)返回非0值;否返回0

2. ferror函数

• 原型:int ferror(FILE *stream);

• 作用:判断文件读写是否发生错误

• 返回值:是(发生错误)返回非0值;否返回0

8.3 正确使用流程(必背)

1. 先通过读写函数的返回值判断文件是否读取结束(如fgetc返回EOF,fgets返回NULL,fread返回0);

2. 若读取结束,再通过feof判断是否是正常到达末尾;

3. 同时通过ferror判断是否是错误导致结束。

8.4 完整示例:判断文件读取结束的原因

#include <stdio.h>
int main()
{
    FILE* pf = fopen("test.txt", "r");
    if (pf == NULL)
    {
        perror("fopen");
        return 1;
    }

    int ch = 0;
    // 1. 通过fgetc返回值判断是否读取结束
    while ((ch = fgetc(pf)) != EOF)
    {
        putchar(ch);  // 正常读取则输出字符
    }
    printf("\\n");
    // 2. 读取结束后,判断原因
    if (feof(pf))
    {
        printf("读取结束:正常到达文件末尾\\n");
    }
    else if (ferror(pf))
    {
        printf("读取结束:文件读写发生错误\\n");
    }

    fclose(pf);
    pf = NULL;
    return 0;
}

九、文件缓冲区——系统级优化(关键概念)

9.1 缓冲区的定义

系统为每个打开的文件,在内存中分配一块临时的内存空间,称为文件缓冲区(分为输入缓冲区、输出缓冲区),是系统为了提高文件读写效率设计的优化机制。

9.2 缓冲区的工作原理

写文件的缓冲区流程

程序调用写函数(fputc/fprintf等)→ 数据先写入内存的输出缓冲区 → 缓冲区满/程序正常退出/调用fflush/fclose → 缓冲区数据一次性写入硬盘文件。

读文件的缓冲区流程

程序调用读函数(fgetc/fscanf等)→ 系统先从硬盘文件读取一批数据到内存的输入缓冲区 → 程序从缓冲区中读取数据 → 缓冲区数据读完后,再从硬盘读取下一批。

9.3 核心函数:fflush——强制刷新缓冲区

函数原型

int fflush(FILE *stream);

作用

强制将输出缓冲区中的数据,立即写入硬盘文件(忽略缓冲区是否已满)

返回值

• 成功:返回0

• 失败:返回非0值

适用场景

需要立即保存数据时使用(如写重要日志、程序长时间运行不退出),避免缓冲区数据因程序崩溃丢失。

示例:强制刷新缓冲区

FILE* pf = fopen("test.txt", "w");
fputc('a', pf);  // 数据先存入缓冲区,未写入硬盘
fflush(pf);      // 强制刷新,数据立即写入硬盘
fclose(pf);
pf = NULL;

9.4 缓冲区的关键注意事项

1. 程序正常退出/调用fclose:系统会自动刷新缓冲区,数据不会丢失;

2. 程序异常崩溃:缓冲区数据未写入硬盘,会永久丢失;

3. fflush仅对输出缓冲区有效,对输入缓冲区无作用;

4. 缓冲区的大小由编译器/操作系统决定(一般为4KB或8KB)。

十、拓展:sprintf + sscanf——内存中的格式化读写

这两个函数并非文件操作函数,但与fprintf/fscanf语法高度一致,用于内存中的格式化数据转换,常与文件操作配合使用,补充说明:

10.1 sprintf:将格式化数据写入内存字符串

• 原型:int sprintf(char *str, const char *format, …);

• 作用:按格式将数据写入字符数组str(内存中),生成格式化字符串

• 示例:将结构体数据转为字符串

#include <stdio.h>
struct Student
{
    char name[20];
    int age;
};
int main()
{
    struct Student s = {"张三", 18};
    char buf[100] = {0};
    // 将结构体数据转为字符串存入buf
    sprintf(buf, "姓名:%s,年龄:%d", s.name, s.age);
    printf("%s\\n", buf);  // 输出:姓名:张三,年龄:18
    return 0;
}

10.2 sscanf:从内存字符串读取格式化数据

• 原型:int sscanf(const char *str, const char *format, …);

• 作用:从字符串str中,按格式读取数据存入指定变量

• 示例:从字符串中读取数据到结构体

#include <stdio.h>
struct Student
{
    char name[20];
    int age;
};
int main()
{
    struct Student s = {0};
    char buf[100] = "姓名:张三,年龄:18";
    // 从buf中按格式读取数据到结构体
    sscanf(buf, "姓名:%s,年龄:%d", s.name, &s.age);
    printf("姓名:%s,年龄:%d\\n", s.name, s.age);  // 输出一致
    return 0;
}

10.3 4个格式化函数对比(核心)

函数 数据流向 操作对象 核心作用
printf 程序 → 屏幕 标准输出流 格式化输出到屏幕 
scanf 键盘 → 程序 标准输入流 格式化读取从键盘
fprintf 程序 → 文件 文件流 格式化输出到文件 
fscanf 文件 → 程序 文件流 格式化读取从文件 
sprintf 程序 → 内存字符串 字符数组 格式化数据转字符串 
sscanf 内存字符串 → 程序 字符数组 字符串转格式化数据 

十一、文件操作的常见错误与避坑指南

1. 忘记判空fopen的返回值:导致操作野指针,程序崩溃;

2. 打开与关闭文件不配对:导致文件泄漏,系统资源被占用;

3. 关闭文件后未置NULL:导致野指针,后续误操作引发未知错误;

4. 滥用"w"模式:误清空原有文件内容,数据无法恢复;

5. fread/fwrite的size/count参数不匹配:导致数据读取错乱;

6. 用feof判断文件是否读取结束:导致死循环或错误判断;

7. 格式化读写的格式不匹配:导致读取的数据错误;

8. 二进制文件用文本模式打开:导致数据读写错误(如换行符转换);

9. 忘记刷新缓冲区:程序崩溃时数据丢失(重要数据需用fflush);

10. 路径书写错误:如Windows下路径用\\需转义(D:\\\\test.txt),或相对路径错误。

十二、文件操作的完整开发流程(必背)

1. 包含头文件:#include <stdio.h>

2. 定义文件指针:FILE* pf = NULL;

3. 打开文件:pf = fopen(文件名, 模式);

4. 判空:if (pf == NULL) { perror("fopen"); return 1; }

5. 读写文件:根据需求选择fputc/fgetc/fprintf/fread等函数;

6. 判断读写结束原因:读取结束后用feof+ferror判断;

7. 关闭文件:fclose(pf);

8. 置NULL:pf = NULL;

9. 返回0:程序正常结束。


C语言文件操作是程序实现数据持久化的核心技能,从基础的文件打开关闭、指针运用,到顺序/随机读写的函数使用,再到缓冲区机制、错误判断的细节把控,每一步都关乎程序的稳定性与数据安全性。本文梳理的知识点与代码示例覆盖了文件操作的全流程核心要点,掌握这些内容,便能灵活实现文件的读写、数据存储与解析需求。

文件操作的关键在于规范流程、注重细节,牢记“打开必判空、读写必校验、关闭必置空”的原则,避开常见的操作误区,就能让文件操作成为开发中的实用利器。后续可结合实际场景多做练习,将知识点转化为实操能力,让程序的数处理更灵活、更高效。

赞(0)
未经允许不得转载:网硕互联帮助中心 » C语言文件操作
分享到: 更多 (0)

评论 抢沙发

评论前必须登录!