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);
参数说明
参数 | 类型 | 含义 |
---|---|---|
filename | const char * | 文件名(包含路径,可为相对或绝对路径) |
mode | const 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
会失败。 - 写入的是单个字符,效率较低,不适合大量数据写入(推荐
fputs
或fwrite
)。 - 不支持写入格式化内容(推荐用
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
转为\n
,ftell()
返回的偏移量不一定等于实际字节数(推荐使用"rb"
或"wb"
二进制模式)。 - 如果使用
fseek()
后马上使用ftell()
,应确保文件未处于错误状态。