目录
1. fputc函数和fgetc函数
fputc函数
在 C 语言中,fputc
函数用于向指定的文件流写入单个字符,是文件字符输出的基础函数。
函数原型
#include <stdio.h>
int fputc(int character, FILE *stream);
参数说明
character
:要写入的字符(虽然声明为int
,但实际只使用低 8 位,即char
范围)。stream
:文件指针(FILE*
类型),指定写入的目标流(如文件流、标准输出流stdout
等)。
返回值
成功:返回写入的字符(以
int
类型表示)。失败:返回
EOF
(通常为-1
,表示写入错误或文件结束)。
基本用法
1. 向文件写入字符
#include <stdio.h>
int main() {
FILE *fp = fopen("test.txt", "w"); // 以只写模式打开文件
if (fp == NULL) {
perror("文件打开失败");
return 1;
}
// 写入单个字符
fputc('A', fp); // 写入字符 'A'
fputc('\n', fp); // 写入换行符
fputc('B', fp); // 写入字符 'B'
fclose(fp);
return 0;
}
运行后,test.txt
文件内容为:
A
B
2. 向标准输出流(屏幕)写入字符
fputc
也可用于标准流,例如向屏幕输出:
#include <stdio.h>
int main() {
// 向 stdout(屏幕)输出字符,等价于 putchar('H')
fputc('H', stdout);
fputc('i', stdout);
fputc('\n', stdout); // 输出换行
return 0;
}
运行后屏幕输出:
Hi
特点与注意事项
逐个字符写入:
fputc
每次只能写一个字符,适合处理文本文件的逐字符输出。缓冲机制:写入文件时,数据会先存入缓冲区,缓冲区满或调用
fclose
/fflush
时才会真正写入磁盘。与
putchar
的关系:putchar(c)
本质是fputc(c, stdout)
的宏定义,专门用于向屏幕输出单个字符。错误处理:需检查返回值是否为
EOF
以判断写入是否成功,例如:if (fputc('C', fp) == EOF) { perror("写入失败"); }
二进制文件适用:
fputc
同样可用于二进制文件(打开模式带b
),直接写入字符的 ASCII 码值(原始字节)。
fgetc函数
在 C 语言中,fgetc
函数用于从指定的文件流中读取一个字符,是进行文件字符输入操作的常用函数。
函数原型
#include <stdio.h>
int fgetc(FILE *stream);
参数说明
stream
:文件指针(FILE*
类型),指向要从中读取字符的文件流。可以是通过 fopen
函数打开的文件流,也可以是标准输入流 stdin
等。
返回值
- 成功:返回读取到的字符(以
int
类型表示,实际使用低 8 位来存储字符的 ASCII 码值 )。 - 失败或到达文件末尾:返回
EOF
(通常被定义为-1
)。
基本用法示例
1. 从文件中读取字符
#include <stdio.h>
int main() {
FILE *fp = fopen("test.txt", "r");
if (fp == NULL) {
perror("文件打开失败");
return 1;
}
int ch;
while ((ch = fgetc(fp)) != EOF) {
putchar(ch); // 将读取到的字符输出到屏幕
}
fclose(fp);
return 0;
}
上述代码中,先以只读模式打开 test.txt
文件,然后使用 fgetc
函数循环读取文件中的字符,直到读取到文件末尾(返回 EOF
),并通过 putchar
函数将读取到的字符逐个输出到屏幕上。
2. 从标准输入流(键盘)读取字符
#include <stdio.h>
int main() {
int ch;
printf("请输入一些字符,按 Ctrl+Z(Windows)或 Ctrl+D(Linux/macOS)结束输入:\n");
while ((ch = fgetc(stdin)) != EOF) {
putchar(ch);
}
return 0;
}
这段代码通过 fgetc
从标准输入流 stdin
中读取用户从键盘输入的字符,并将其输出到屏幕上。在 Windows 系统中,用户按下 Ctrl+Z
再按回车键表示输入结束;在 Linux 和 macOS 系统中,按下 Ctrl+D
表示输入结束。
注意事项
1.错误判断:在使用 fgetc
读取字符后,一定要检查返回值是否为 EOF
,以此判断是读取到文件末尾还是发生了读取错误。
2.类型转换:虽然 fgetc
的返回值是 int
类型,但在将返回值赋值给 char
类型变量时,需要进行合适的类型转换,并且要注意处理 EOF
的情况,避免误将 EOF
当作普通字符处理。
3.缓冲区影响:文件流存在缓冲区,fgetc
读取字符时,先从缓冲区获取数据。当缓冲区为空时,才会真正从磁盘文件中读取数据填充缓冲区。
4.二进制文件读取:fgetc
也可以用于读取二进制文件,但由于 EOF
也是一个合法的返回值,所以在读取二进制文件时,不能单纯通过判断返回值是否为 EOF
来判断读取是否结束,通常需要结合文件大小等其他信息来判断。
2. feof和ferror函数
在 C 语言文件操作中,feof
和ferror
是用于判断文件操作状态的两个重要函数,前者主要用于检测文件是否到达结尾,后者用于判断文件操作过程中是否出现错误,以下是详细介绍:
feof 函数
函数原型
#include <stdio.h>
int feof(FILE *stream);
参数说明:
stream
是一个文件指针,指向要检测状态的文件流,这个文件流通常是通过fopen
函数打开文件后返回的指针。返回值
文件未到达结尾:函数返回 0。
文件到达结尾:函数返回一个非零值(通常是 1)。
使用场景及示例
在使用循环从文件中读取数据时,判断是否读取到文件末尾。
#include <stdio.h>
int main() {
FILE *fp = fopen("test.txt", "r");
if (fp == NULL) {
perror("文件打开失败");
return 1;
}
int ch;
while (!feof(fp)) {
ch = fgetc(fp);
putchar(ch);
}
fclose(fp);
return 0;
}
上述代码中,通过feof
函数判断是否到达文件末尾,若未到达则继续读取字符并输出。但需要注意,feof
函数是在读取操作后才更新文件结束标志,因此在读取文件时,不能单纯依赖feof
函数作为循环读取的条件,因为可能会多读一次,更好的做法是先读取,再判断返回值是否为EOF
,如:
#include <stdio.h>
int main() {
FILE *fp = fopen("test.txt", "r");
if (fp == NULL) {
perror("文件打开失败");
return 1;
}
int ch;
while ((ch = fgetc(fp)) != EOF) {
putchar(ch);
}
fclose(fp);
return 0;
}
ferror 函数
函数原型
#include <stdio.h>
int ferror(FILE *stream);
参数说明:
stream
同样是一个文件指针,指向要检测状态的文件流。返回值
文件操作未出错:函数返回 0。
文件操作出现错误:函数返回一个非零值(通常是 1)。
使用场景及示例
在对文件进行读写操作后,判断是否出现错误。
#include <stdio.h>
int main() {
FILE *fp = fopen("test.txt", "w");
if (fp == NULL) {
perror("文件打开失败");
return 1;
}
if (fputs("Hello, world!", fp) == EOF) {
if (ferror(fp)) {
perror("写入文件时出错");
}
}
fclose(fp);
return 0;
}
上述代码中,在使用fputs
向文件写入数据后,判断返回值是否为EOF
,若为EOF
则调用ferror
函数判断是否是因为写入错误导致的,若是则输出错误信息。
注意事项
feof
函数:不能在未进行读取操作前就用它判断文件是否结束,要结合实际读取操作和返回值判断。ferror
函数:它检测的是最近一次文件操作是否出错,在进行新的文件操作前,需要根据需要进行错误处理,例如可以使用clearerr
函数清除错误标志,以便后续正确检测文件操作状态。
clearerr(fp); // 清除文件流fp的错误和文件结束标志
3. fputs函数和fgets函数
在 C 语言中,fputs
和 fgets
是用于字符串级文件读写的标准库函数,分别用于向文件流写入字符串和从文件流读取字符串,比单字符操作的 fputc
/fgetc
更高效。
fputs
函数
向文件流写入字符串
函数原型
#include <stdio.h>
int fputs(const char *str, FILE *stream);
参数说明
str
:要写入的字符串(以\0
结尾的字符数组)。stream
:目标文件流(如文件指针、stdout
等)。
返回值
成功:返回非负值(通常为非零)。
失败:返回
EOF
(-1
)。
特点与用法
写入规则:
只写入字符串中的有效字符(从首字符到\0
前的字符),不自动添加换行符,也不写入\0
本身。示例 1:向文件写入字符串
#include <stdio.h> int main() { FILE *fp = fopen("test.txt", "w"); if (fp == NULL) { perror("文件打开失败"); return 1; } // 写入字符串(需手动添加换行符) fputs("Hello, fputs!", fp); fputs("\n这是第二行", fp); // 手动加 '\n' 换行 fclose(fp); return 0; }
结果(
test.txt
):Hello, fputs! 这是第二行
示例 2:向标准输出流(屏幕)写入
等价于puts
但不自动换行:fputs("Hello, stdout!", stdout); // 输出到屏幕,无换行 fputs("\n", stdout); // 手动换行
fgets
函数
从文件流读取字符串
函数原型
#include <stdio.h>
char *fgets(char *str, int size, FILE *stream);
参数说明
str
:用于存储读取结果的字符数组。size
:最大读取长度(包含结尾的\0
,实际最多读size-1
个字符)。stream
:源文件流(如文件指针、stdin
等)。
返回值
成功:返回
str
(读取到的字符串的地址)。失败或文件结束:返回
NULL
。
特点与用法
读取规则:
最多读取
size-1
个字符,或遇到换行符\n
时停止(会保留换行符)。读取结束后自动在末尾添加
\0
,确保字符串完整。
示例 1:从文件读取字符串
#include <stdio.h> int main() { FILE *fp = fopen("test.txt", "r"); if (fp == NULL) { perror("文件打开失败"); return 1; } char buffer[100]; // 循环读取,直到文件结束 while (fgets(buffer, 100, fp) != NULL) { printf("读取到:%s", buffer); // buffer 已包含换行符 } fclose(fp); return 0; }
若
test.txt
内容为:第一行内容 第二行内容
输出结果:
读取到:第一行内容 读取到:第二行内容
示例 2:从标准输入流(键盘)读取
可读取一行输入(含空格):char input[50]; printf("请输入一行文字:"); fgets(input, 50, stdin); // 读取键盘输入,最多49个字符 printf("你输入了:%s", input);
三、注意事项
fputs
的坑点:不会自动添加换行符,需手动写入
\n
。若字符串中包含
\0
(如二进制数据),会提前终止写入(只写\0
前的内容)。
fgets
的坑点:若一行内容超过
size-1
,会截断读取(剩余内容留在流中,下次读取继续)。会保留换行符
\n
,如需去除可手动处理:
// 去除 fgets 读取的换行符
char buffer[100];
fgets(buffer, 100, fp);
buffer[strcspn(buffer, "\n")] = '\0'; // 用 \0 替换 \n
读取到文件末尾或错误时返回 NULL
,需结合 feof
/ferror
判断原因。
与
puts
/gets
的区别:puts(str)
等价于fputs(str, stdout) + fputs("\n", stdout)
(自动换行)。gets
因无长度限制存在缓冲区溢出风险,已被弃用,推荐用fgets
替代。
4. fprintf函数和fscanf函数
在 C 语言中,fprintf
函数和fscanf
函数是用于格式化输入输出的函数,和标准输入输出函数printf
、scanf
功能类似,区别在于fprintf
和fscanf
操作的是文件流,可以实现向文件中进行格式化写入以及从文件中格式化读取数据。
fprintf 函数
fprintf
函数用于向指定的文件流中按照指定的格式写入数据。
函数原型
#include <stdio.h>
int fprintf(FILE *stream, const char *format, ...);
参数说明
stream
:指向要写入数据的文件流,可以是通过fopen
函数打开的文件指针,也可以是标准输出流stdout
(表示输出到屏幕)、标准错误流stderr
。format
:格式化字符串,用于指定输出数据的格式,包含普通字符、转义字符以及格式说明符,如%d
(表示整数)、%f
(表示浮点数)、%s
(表示字符串)等。...
:可变参数列表,根据format
中格式说明符的数量和类型,传入相应的要输出的数据。
返回值
成功:返回实际写入的字符个数。
失败:返回一个负数。
示例代码
#include <stdio.h>
int main() {
FILE *fp = fopen("test.txt", "w");
if (fp == NULL) {
perror("文件打开失败");
return 1;
}
int num = 10;
float fnum = 3.14;
char str[] = "Hello";
// 向文件中格式化写入数据
int written_count = fprintf(fp, "整数: %d, 浮点数: %f, 字符串: %s\n", num, fnum, str);
if (written_count < 0) {
perror("写入失败");
}
fclose(fp);
return 0;
}
上述代码以写入模式打开文件test.txt
,然后使用fprintf
将整数、浮点数和字符串按照指定格式写入文件中,最后关闭文件。
fscanf 函数
fscanf
函数用于从指定的文件流中按照指定的格式读取数据。
函数原型
#include <stdio.h>
int fscanf(FILE *stream, const char *format, ...);
参数说明
stream
:指向要读取数据的文件流,通常是通过fopen
函数打开的文件指针,也可以是标准输入流stdin
(表示从键盘读取)。format
:格式化字符串,用于指定读取数据的格式,与fprintf
中的format
类似,包含格式说明符等。...
:可变参数列表,是指向用于存储读取数据的变量的指针,参数的数量和类型要与format
中的格式说明符相匹配。
返回值
成功:返回成功匹配和赋值的输入项的个数。
失败:返回
EOF
(通常为-1
)。
示例代码
#include <stdio.h>
int main() {
FILE *fp = fopen("test.txt", "r");
if (fp == NULL) {
perror("文件打开失败");
return 1;
}
int num;
float fnum;
char str[20];
// 从文件中格式化读取数据
int read_count = fscanf(fp, "整数: %d, 浮点数: %f, 字符串: %s", &num, &fnum, str);
if (read_count == EOF) {
perror("读取失败");
} else {
printf("读取到的数据:整数: %d, 浮点数: %f, 字符串: %s\n", num, fnum, str);
}
fclose(fp);
return 0;
}
上述代码以读取模式打开文件test.txt
,然后使用fscanf
按照指定格式从文件中读取整数、浮点数和字符串,根据读取结果进行相应处理,最后关闭文件。
注意事项
文件打开模式:使用
fprintf
写入文件时,文件需以写模式(如"w"
、"w+"
等)打开;使用fscanf
读取文件时,文件需以读模式(如"r"
、"r+"
等)打开。格式匹配:
fprintf
和fscanf
中format
里的格式说明符要与实际数据类型严格匹配,否则可能导致写入或读取的数据不正确,甚至程序崩溃。缓冲区影响:文件流存在缓冲区机制,读写操作可能不会立即反映到文件中,在合适的时候(如关闭文件前)可以使用
fflush
函数刷新缓冲区,确保数据正确写入文件。错误处理:要根据函数的返回值及时判断操作是否成功,并进行相应的错误处理,以增强程序的健壮性。
5. fread函数和fwrite函数
在 C 语言中,fread
和 fwrite
是用于二进制文件读写的核心函数,支持对任意数据类型(如结构体、数组等)进行块级读写,比字符 / 字符串级函数(如 fputc
/fgets
)更高效,尤其适合处理二进制数据(如图片、音频、自定义结构体等)。
fwrite
函数
向文件流写入二进制数据
函数原型
#include <stdio.h>
size_t fwrite(const void *ptr, size_t size, size_t count, FILE *stream);
参数说明
ptr
:指向要写入数据的内存地址(源数据的起始地址)。size
:单个数据单元的字节大小(如sizeof(int)
表示每个 int 占 4 字节)。count
:要写入的数据单元数量(如写入 3 个 int,则 count=3)。stream
:目标文件流(需以二进制模式打开,如"wb"
/"ab"
等)。
返回值
成功:返回实际写入的数据单元数量(小于等于 count)。
失败 / 写入不完整:返回值小于 count(需结合
ferror
判断错误)。
特点与用法
二进制写入:直接按内存中的原始字节写入,不进行任何格式转换(如不转换换行符),适合非文本数据。
批量操作:一次可写入多个数据单元(如数组、结构体数组),效率高。
示例 1:写入单个结构体
#include <stdio.h>
// 定义一个结构体
typedef struct {
int id;
char name[20];
float score;
} Student;
int main() {
FILE *fp = fopen("students.dat", "wb"); // 二进制写模式
if (fp == NULL) {
perror("文件打开失败");
return 1;
}
Student s = {101, "ZhangSan", 95.5f};
// 写入1个Student类型数据(每个占sizeof(Student)字节)
size_t written = fwrite(&s, sizeof(Student), 1, fp);
if (written != 1) {
perror("写入失败");
}
fclose(fp);
return 0;
}
示例 2:写入数组
// 写入一个int数组
int nums[] = {1, 2, 3, 4, 5};
int len = sizeof(nums) / sizeof(nums[0]);
// 写入5个int(每个4字节)
fwrite(nums, sizeof(int), len, fp);
fread
函数
从文件流读取二进制数据
函数原型
#include <stdio.h>
size_t fread(void *ptr, size_t size, size_t count, FILE *stream);
参数说明
ptr
:指向用于存储读取数据的内存地址(目标缓冲区)。size
:单个数据单元的字节大小(同fwrite
)。count
:计划读取的数据单元数量。stream
:源文件流(需以二进制读模式打开,如"rb"
/"rb+"
等)。
返回值
成功:返回实际读取的数据单元数量(等于 count 表示读满,小于 count 可能是文件结束或出错)。
失败:返回 0(需结合
feof
/ferror
判断是文件结束还是错误)。
特点与用法
二进制读取:按原始字节读取,与写入时的内存布局完全一致,确保数据完整还原。
精准还原:可直接读取结构体、数组等复杂类型,无需手动解析格式。
示例:读取之前写入的结构体
#include <stdio.h>
typedef struct {
int id;
char name[20];
float score;
} Student;
int main() {
FILE *fp = fopen("students.dat", "rb"); // 二进制读模式
if (fp == NULL) {
perror("文件打开失败");
return 1;
}
Student s;
// 读取1个Student类型数据
size_t read = fread(&s, sizeof(Student), 1, fp);
if (read == 1) {
// 成功读取,打印数据
printf("ID: %d, Name: %s, Score: %.1f\n", s.id, s.name, s.score);
} else if (feof(fp)) {
printf("已到达文件末尾\n");
} else if (ferror(fp)) {
perror("读取失败");
}
fclose(fp);
return 0;
}
三、关键注意事项
必须用二进制模式打开文件:
读写时需指定b
模式(如"wb"
/"rb"
),否则在 Windows 系统中可能因换行符转换破坏二进制数据。数据一致性:
写入和读取时的size
和数据类型必须完全一致(如写入sizeof(Student)
,读取也必须用sizeof(Student)
),否则会导致数据解析错误。返回值判断:
不能仅通过返回值是否为 0 判断失败(可能是文件结束),需用
feof
区分 “文件结束” 和 “读取错误”。- 示例:
size_t n = fread(buf, size, count, fp); if (n < count) { if (feof(fp)) { // 正常结束:已读到文件末尾 } else if (ferror(fp)) { // 读取错误 } }
跨平台兼容性:
结构体在不同编译器 / 平台可能有字节对齐差异,如需跨平台传输,需手动处理对齐(如用#pragma pack
)。与文本函数的区别:
fwrite
/fread
适合二进制数据(图片、音频、结构体等),按字节精准操作。fprintf
/fscanf
适合文本数据,会进行格式转换(如整数转字符串)。