文章

C 文件操作

C 文件操作

在编程中,我们可能需要多次生成某些特定的输入数据。有时,仅在控制台上显示数据是不够的。要显示的数据可能非常大,在控制台上只能显示有限的数据,而且由于内存是容易丢失的,不可能一次又一次地恢复程序生成的数据。 但是,如果我们需要这样做,我们可以将其存储到本地文件系统中,该文件系统是易失的,并且每次都可以访问。 在这里,需要在 C 中处理文件。

注意,使用文件操作函数前必须先引入标准IO库

1
#include <stdio.h>

C 中的文件处理使我们能够通过我们的 C 程序创建、更新、读取和删除存储在本地文件系统中的文件。 可以对文件执行以下操作:

  • 创建新文件
  • 打开现有文件
  • 从文件中读取
  • 写入文件
  • 删除文件

文件处理函数

C 库中有许多函数可以打开、读取、写入、搜索和关闭文件。 文件函数列表如下:

函数描述 Description
fopen()打开新文件或现有文件
fprintf()将数据写入文件
fscanf()从文件中读取数据
fputc()将一个字符写入文件
fgetc()从文件中读取一个字符
fputs()将一整串字符写入文件
fgets()从文件中读取一整串字符
fclose()关闭文件
fseek()将文件指针设置到给定位置
ftell()返回当前位置
rewind()将文件指针设置为文件的开头

fopen()

要对文件进行操作,必须先用fopen()打开一个文件,并返回一个文件指针 FILE*,供后续读写使用。

1
FILE *fopen(const char *filename, const char *mode);

参数说明

参数类型含义
filenameconst char *文件名(包含路径,可为相对或绝对路径)
modeconst char *打开模式,决定读/写/追加等方式

模式mode说明

模式含义文件不存在时文件存在时特点
"r"只读方式打开打开失败从头读取文件必须存在
"w"只写方式打开创建新文件清空原内容文件会被清空
"a"追加写方式打开创建新文件写在文件末尾不清空文件
"r+"读写方式打开打开失败可读可写不清空文件
"w+"读写方式打开创建新文件清空原内容会清空原内容
"a+"读/追加写方式打开创建新文件可读,从尾部写只能从末尾写

所有模式都可以加 b 表示二进制打开(如 "rb""wb+""ab+"),用于处理非文本文件(如图片、音频)

返回值

  • 返回值是类型为 FILE * 的文件指针。
  • 若打开失败,返回 NULL
  • 可以用 perror()strerror(errno) 获取具体失败原因。
1
2
3
4
FILE *fp = fopen("data.txt", "r");
if (fp == NULL) {
    perror("fopen failed");
}

fprintf()

fprintf 是 C 标准库中用于 格式化输出到文件 的函数,功能和 printf 类似,但输出的不是终端,而是指定的文件。

1
int fprintf(FILE *stream, const char *format, ...);

参数说明

参数含义
stream文件指针(由 fopen 打开)
format格式字符串(类似 printf 的用法)
...可变参数,根据 format 对应输出

返回值

  • 返回成功写入的字符数(不包括结尾的 \0)。
  • 出错时返回一个负数

示例

1
2
3
4
5
6
7
8
9
#include <stdio.h>  
int main() {
    FILE *fp;
    char text[] = "fprint";
    fp = fopen("file.txt", "w"); // 打开文件
    fprintf(fp, "Hello file by %s...\n", text); // 将数据写入文件
    fclose(fp); // 关闭文件  
    return 0;
} 

fscanf()

fscanf 是 C 标准库中用于从文件中格式化读取数据的函数,它的功能类似于 scanf,但从指定文件中读取内容,而不是从终端输入。

1
int fscanf(FILE *stream, const char *format, ...);

参数说明

参数含义
stream文件指针(由 fopen 打开)
format格式字符串,说明要读取的数据格式(如 %d %f
...指向变量的指针,用来存储读取到的数据

返回值

  • 正整数:成功读取并赋值的字段个数(不等于读取的字符数)
  • EOF:读取失败或者到达文件末尾,通常为-1

示例

假设有两个文件

1
25 88.5
1
Hello World!!!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#include <stdio.h>  

int main() {  
    int age;
    float score;
    char text1[100];
    char text2[100];

    FILE *fp1 = fopen("number.txt", "r"); // 打开文件
    if (fp1 == NULL) {
        printf("Error opening file\n");
        return 1;
    }
   
    if (fscanf(fp1, "%d %f", &age, &score) == 2) {
        printf("Age: %d, Score: %.2f\n", age, score);
    } else {
        printf("Failed to read data.\n");
    }

    FILE *fp2 = fopen("strings.txt", "r"); // 打开文件
    if (fp2 == NULL) {
        printf("Error opening file\n");
        return 1;
    }

    if (fscanf(fp2, "%s %s", text1, text2) == 2) {
        printf("Success to read data: %s  %s\n", text1, text2);
    } else {
        printf("Failed to read data.\n");
    }
    
    fclose(fp1); // 关闭文件  
    fclose(fp2); // 关闭文件  
} 

注意事项

  • 参数必须是指针或字符串数组,如 &age,否则不能正确赋值。
  • fscanf 会跳过空格、换行、制表符等空白字符。
  • 不要对格式不确定的文件使用 fscanf,可能读取错误。
  • fscanf 会从当前位置开始读取,所以配合 rewind()fseek() 时要注意。
  • 如果读取失败返回 EOF,可以通过 feof(fp)ferror(fp) 进一步判断。

fputc()

fputc 是 C 标准库中用于向文件中写入单个字符的函数。它适合用来低级别逐字符写文件,比如写文本、生成字符流等。

1
int fputc(int character, FILE *stream);

参数说明

参数含义
character要写入的字符(会被自动转换为 unsigned char
stream文件指针,指定写入的目标文件(由 fopen 打开)

返回值

返回值含义
成功返回该字符表示写入成功
EOF-1写入失败,可用 perror() 调试

参考示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <stdio.h>
#include <string.h>

int main() {
    FILE *fp = fopen("file.txt", "w");
    if (fp == NULL) {
        printf("Error opening file\n");
        return 1;
    }

    char inputtext[] = "Hello World";

    // or for (int i = 0; text[i] != '\0'; i++) {
    for (int i = 0; i < strlen(inputtext); i++) {
        fputc(inputtext[i], fp);
    }

    fclose(fp);
    return 0;
}

注意事项

  • 可以使用fputc(char, stdout),将字符输出在终端,效果同putchar(char)
  • 必须保证文件指针有效(成功打开),否则 fputc 会失败。
  • 写入的是单个字符,效率较低,不适合大量数据写入(推荐 fputsfwrite)。
  • 不支持写入格式化内容(推荐用 fprintf)。
  • 写完之后要及时 fclose(fp)fflush(fp) 刷新缓冲区。

fgetc()

fgetc 是 C 标准库中用于从文件中读取单个字符的函数。它是最基本的文件读取方法之一,常用于逐字符读取文本文件内容。

1
int fgetc(FILE *stream);

返回值

返回值含义
成功:返回字符字符会被提升为 int 类型(ASCII 值)
失败:返回 EOF读到文件末尾或出错

用法示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <stdio.h>

int main() {
    FILE *fp = fopen("example.txt", "r");
    if (!fp) {
        perror("Failed to open file");
        return 1;
    }

    int ch;
    int count = 0;
    while ((ch = fgetc(fp)) != EOF) {
        putchar(ch);  // 输出字符到终端
        count++;    // 统计字符数
    }

    fclose(fp);
    return 0;
}

注意事项

  • 返回类型是 int,不能直接用 char 接收,否则读不到 EOF,会出错!
  • 必须用 != EOF 判断是否读完。
  • 用于文本文件,不适合大文件或二进制数据(推荐 fread)。
  • fgets 不同,fgetc 不会一次读取一行,只读一个字符。

fputs()

fputs 是 C 标准库中用于向文件中写入字符串的函数,和 fputc 类似,但一次可以写入一整串字符(字符串),效率更高,适合写文本内容。

1
int fputs(const char *str, FILE *stream);

参数说明

参数含义
str要写入的字符串(结尾以 \0 终止,不会写入 \0
stream文件指针(由 fopen 打开,用于写入的文件)

返回值

返回值含义
≥ 0写入成功
EOF写入失败(可用 perror() 调试)

程序示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <stdio.h>

int main() {
    FILE *fp = fopen("output.txt", "w");
    if (!fp) {
        perror("Failed to open file");
        return 1;
    }

    fputs("Hello, world!\n", fp);
    fputs("This is fputs example.\n", fp);

    fclose(fp);
    return 0;
}

注意事项

  • fputs 不会自动添加换行符,需要手动加 \n
  • 写入失败不会自动报错,建议检查返回值或使用 ferror()
  • 不会写入字符串末尾的 \0
  • 文件需以 "w", "a""r+" 等可写模式打开。

fgets()

fgets 是 C 标准库中用于从文件或标准输入中读取一行字符串的函数,它能安全地防止缓冲区溢出,是比 gets 更推荐的读取方式。

1
char *fgets(char *str, int n, FILE *stream);

参数说明

参数含义
str字符数组,用于存储读取的字符串(输出缓冲区)
n最多读取字符数(包含结尾的 \0),即缓冲区大小
stream文件指针(如 stdin 表示标准输入,或 fopen 打开的文件)

返回值

返回值含义
非 NULL 指针表示读取成功,返回的就是 str 指针
NULL读取失败或读到 EOF(例如文件结束)

程序示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <stdio.h>

int main() {
    char buffer[100];
    FILE *fp = fopen("test.txt", "r");
    if (!fp) {
        perror("Failed to open file");
        return 1;
    }

    while (fgets(buffer, sizeof(buffer), fp)) {
        printf("Line: %s", buffer);  // 不需要加 \n,fgets 会保留换行
    }

    fclose(fp);
    return 0;
}

注意事项

  • fgets 最多读取 n - 1 个字符,第 n 个字符为 \0
  • 会保留换行符 \n,除非读入的内容刚好填满缓冲区。
  • 如果读到文件末尾(或错误),返回 NULL
  • 不会跳过空白符(与 scanf 不同)。
  • 可配合 strtok()sscanf() 等处理读取的字符串内容。

fclose()

fclose() 函数用于关闭文件。每次用 fopen 打开文件后,必须调用 fclose 来释放资源、刷新缓冲区,确保数据正确写入磁盘

fclose() 函数的语法如下:

1
int fclose(FILE *stream);

返回值

  • 0:成功关闭文件
  • EOF:关闭失败(返回 -1),可用 perror() 查看原因

fseek()

fseek() 是 C 标准库中用于在文件中移动文件指针的位置的函数。它是处理文件读写时的核心函数之一,尤其适用于二进制文件,可以跳过特定内容,或回到文件某个位置重新读取/写入。

1
int fseek(FILE *stream, long offset, int whence);

参数说明

参数说明
stream打开的文件指针(例如通过 fopen() 返回的 FILE *
offset相对偏移量(单位是字节
whence参考位置,有三个宏定义取值,分别如下:
宏定义说明
SEEK_SET从文件开头偏移(offset 从 0 开始)
SEEK_CUR从当前文件位置偏移
SEEK_END从文件末尾开始偏移

返回值

  • 成功:返回 0
  • 失败:返回非 0,并可使用 perror()ferror() 检查错误原因

用法示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <stdio.h>

int main() {
    FILE *fp = fopen("data.bin", "rb");
    if (!fp) {
        perror("open");
        return 1;
    }

    int value;
    fseek(fp, sizeof(int), SEEK_SET);  // 跳过前 1 个 int(4 字节)
    fread(&value, sizeof(int), 1, fp);
    printf("第二个整数是: %d\n", value);

    fclose(fp);
    return 0;
}

注意事项

  • fseek() 不能用于以文本模式打开的文件(在某些系统上可能无法精确定位)。
  • 使用 "rb", "wb" 等二进制模式更稳定。
  • 使用 fseek() 后若进行读/写操作,推荐配合 fflush()(写前)或 rewind()(回到开头)确保文件状态一致。

rewind()

rewind() 是 C 标准库中一个简单实用的函数,作用是将文件指针移动到文件的开头,通常用于在读取或写入过程中需要“重新开始”的场景。

1
void rewind(FILE *stream);

功能说明

  • FILE * 所指向的文件指针重置到文件起始位置(字节位置 0)
  • 同时清除文件的错误标志和 EOF 标志(等同于调用 clearerr(stream)

使用场景

  • 在读取文件时需要“从头开始读第二遍”
  • 写入文件后想要重头再读内容
  • 替代 fseek(fp, 0, SEEK_SET) 的简洁版本(但 rewind() 不返回值)

注意

  • rewind() 不返回错误码,无法检测是否成功,但如果文件关闭或无效,则后续操作也会失败。
  • 只适用于已打开的 FILE *,不能用于 NULL 指针。

ftell()

ftell() 是 C 语言标准库中用于获取当前文件指针位置的函数。配合 fseek()rewind() 一起使用,可以方便地在文件中定位和跳转。

1
long ftell(FILE *stream);

功能说明

  • 返回当前文件指针(读/写位置)距离文件开头的字节数
  • 通常与 fseek() 搭配使用,可用于记录、还原文件位置

返回值

情况返回值
成功当前偏移量(long 类型)
失败-1L,并设置 errno

使用场景

用法场景示例
获取当前位置(备份)long pos = ftell(fp);
回到之前位置fseek(fp, pos, SEEK_SET);
计算文件大小(常用)fseek(fp, 0, SEEK_END); size = ftell(fp);

使用示例

  • 查看文件当前位置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <stdio.h>

int main() {
    FILE *fp = fopen("example.txt", "r");
    if (!fp) {
        perror("open");
        return 1;
    }

    fgetc(fp);  // 读入 1 个字符

    long pos = ftell(fp);
    if (pos != -1L) {
        printf("当前位置字节偏移: %ld\n", pos);
    } else {
        perror("ftell error");
    }

    fclose(fp);
    return 0;
}
  • 获取文件大小
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <stdio.h>

int main() {
    FILE *fp = fopen("example.txt", "r");
    if (!fp) {
        perror("open");
        return 1;
    }

    fseek(fp, 0, SEEK_END);

    long pos = ftell(fp);
    if (pos != -1L) {
        printf("当前位置字节偏移: %ld\n", pos);
    } else {
        perror("ftell error");
    }

    fclose(fp);
    return 0;
}

注意事项

  • 在 文本模式("r""w" 等)下,某些系统(如 Windows)可能将 \r\n 转为 \nftell() 返回的偏移量不一定等于实际字节数(推荐使用 "rb""wb" 二进制模式)。
  • 如果使用 fseek() 后马上使用 ftell(),应确保文件未处于错误状态。
本文由作者按照 CC BY 4.0 进行授权