C语言基础08——文件的输入与输出
一、文件的基本知识
1.1 文件的定义
文件(file)一般指存储在外部介质上数据的集合。一批数据是以文件的形式存放在外部介质(如磁盘)上的。操作系统是以文件为单位对数据进行管理的,也就是说,如果想找存放在外部介质上的数据,必须先按文件名找到所指定的文件,然后再从该文件中读取数据。要向外部介质上存储数据也必须先建立一个文件(以文件名作为标志),才能向它输出数据。
1.2 文件的分类
根据数据的组织形式,数据文件可分为文本文件与二进制文件。
(1)文本文件:数据以字符形式存储,每个字符对应 ASCII 码(如整数 123 存储为 '1'、'2'、'3' 三个字符),可被文本编辑器直接读取。
(2)二进制文件:数据按内存中的存储形式直接存储(如整数 123 存储为二进制01111011),节省空间但不可直接被文本编辑器解读。
1.3 文件指针
C 语言通过文件指针(FILE*类型)操作文件,它指向一个包含文件信息(如文件名、位置指针等)的结构体。例如:
FILE *fp; // 定义文件指针fp
为方便起见,通常将这种指向文件信息区的指针变量简称为指向文件的指针变量。
注意:指向文件的指针变量并不是指向外部介质上的数据文件的开头,而是指向内存中的文件信息区的开头。
二、文件的打开与关闭
文件操作需遵循 “打开→操作→关闭” 的流程。
2.1 打开文件:fopen 函数
fopen函数功能:打开指定文件并返回文件指针。
fopen 函数的调用方式为:
fopen (文件名,使用文件方式);
例如:
fopen ("a1","r");
表示要打开名字为 a1 的文件,使用文件方式为 “读入”(r 代表 read,即读入)。fopen 函数的返回值是指向 a1 文件的指针(即 a1 文件信息区的起始地址)。通常将fopen 函数的返回值赋给一个指向文件的指针变量。如:
FILE *fp; // 定义一个指向文件的指针变量 fp
fp = fopen ("a1","r"); // 将 fopen 函数的返回值赋给指针变量 fp
这样 fp 就和文件 a1 相联系了,或者说,fp 指向了 a1 文件。
可以看出,在打开一个文件时,通知编译系统以下 3 个信息:
(1)需要打开文件的名字,也就是准备访问的文件的名字;
(2)使用文件的方式(“读” 还是 “写” 等);
(3)让哪一个指针变量指向被打开的文件。
示例:
FILE *fp = fopen("test.txt", "r"); // 以只读方式打开文本文件
if (fp == NULL) { // 打开失败时返回NULL,必须判断printf("文件打开失败!");exit(1); // 退出程序
}
(1)filename
:文件名(含路径,如"data.txt"
或"C:\\test.bin"
)。
(2)mode
:打开方式(核心参数)。
使用文件方式如下表:
文件使用方式 | 含 义 | 如果指定的文件不存在 |
---|---|---|
r(只读) | 为了输入数据,打开一个已存在的文本文件 | 出错 |
w(只写) | 为了输出数据,打开一个文本文件 | 建立新文件 |
a(追加) | 向文本文件尾添加数据 | 出错 |
rb(只读) | 为了输入数据,打开一个二进制文件 | 出错 |
wb(只写) | 为了输出数据,打开一个二进制文件 | 建立新文件 |
ab(追加) | 向二进制文件尾添加数据 | 出错 |
"r+"(读写) | 为了读和写,打开一个文本文件 | 出错 |
"w+"(读写) | 为了读和写,建立一个新的文本文件 | 建立新文件 |
"a+"(读写) | 为了读和写,打开一个文本文件 | 出错 |
"rb+"(读写) | 为了读和写,打开一个二进制文件 | 出错 |
"wb+"(读写) | 为了读和写,建立一个新的二进制文件 | 建立新文件 |
"ab+"(读写) | 为读写打开一个二进制文件 | 出错 |
2.2 关闭文件:fclose () 函数
功能:关闭文件,释放资源,避免数据丢失。
格式:
int fclose(FILE *fp);
返回值:成功返回 0,失败返回 EOF。
示例:
fclose(fp); // 关闭文件
fp = NULL; // 避免野指针
三、文本文件的读写
文本文件以字符或字符串为单位读写,常用函数如下:
3.1字符读写:fgetc () 与 fputc ()
fgetc(fp)
:从fp
指向的文件读取一个字符,返回字符 ASCII 值(读到末尾返回 EOF)。
fputc(ch, fp)
:将字符ch
写入fp
指向的文件,成功返回该字符,失败返回 EOF。
示例:复制文本文件
FILE *in, *out;
char ch;
in = fopen("source.txt", "r");
out = fopen("dest.txt", "w");
while ((ch = fgetc(in)) != EOF) { // 读取到末尾时退出fputc(ch, out); // 写入目标文件
}
fclose(in);
fclose(out);
3.2 字符串读写:fgets () 与 fputs ()
fgets(str, n, fp)
:从fp
读取最多n-1
个字符到str
,遇到换行或 EOF 停止,自动添加 '\0'。
fputs(str, fp)
:将字符串str
(不含 '\0')写入fp
,成功返回非负整数,失败返回 EOF。
示例:读取并显示文本文件内容
char buf[100];
FILE *fp = fopen("test.txt", "r");
while (fgets(buf, 100, fp) != NULL) { // 读取一行fputs(buf, stdout); // 输出到屏幕(stdout是标准输出指针)
}
fclose(fp);
3.3 格式化读写:fscanf () 与 fprintf ()
类似scanf()
和printf()
,但操作对象是文件。
fscanf(fp, 格式串, 参数列表)
:从文件按格式读取数据。
fprintf(fp, 格式串, 参数列表)
:按格式向文件写入数据。
示例:向文件写入学生信息
FILE *fp = fopen("students.txt", "w");
char name[20] = "张三";
int age = 20;
float score = 90.5;
fprintf(fp, "%s %d %.1f", name, age, score); // 写入文件
fclose(fp);
四、二进制文件的读写
二进制文件以数据块为单位读写,常用fread()
和fwrite()
函数。
4.1 fread () 与 fwrite () 函数
格式:
// 从文件读取数据size_t fread(void *ptr, size_t size, size_t count, FILE *fp);// 向文件写入数据size_t fwrite(const void *ptr, size_t size, size_t count, FILE *fp);
ptr:存放数据的内存地址(读时是目标地址,写时是源地址)。
size:每个数据项的字节数(如sizeof(int))。
count:数据项个数。
返回值:成功读写的数据项个数(非字节数)。
4.2 示例:用二进制文件存储结构体数组
struct Student {char name[20];int age;};int main() {struct Student s[2] = {{"张三", 20}, {"李四", 21}};FILE *fp = fopen("stu.bin", "wb"); // 二进制写fwrite(s, sizeof(struct Student), 2, fp); // 写入2个学生fclose(fp);// 读取二进制文件struct Student read_s[2];fp = fopen("stu.bin", "rb"); // 二进制读fread(read_s, sizeof(struct Student), 2, fp);printf("%s %d\n", read_s[0].name, read_s[0].age); // 输出:张三 20fclose(fp);return 0;}
五、文件的定位与随机读写
文件内部有一个位置指针,记录下一次读写的位置。默认情况下,读写操作后位置指针自动后移,也可手动调整实现随机读写。
5.1 ftell ():获取当前位置
返回位置指针相对于文件开头的字节数,失败返回 - 1L。
long pos = ftell(fp); // 获取当前位置
5.2 fseek ():移动位置指针
格式:
int fseek(FILE *fp, long offset, int origin);
offset:偏移量(正数表示向后移,负数表示向前移)。
origin:起始位置:
SEEK_SET(0):文件开头
SEEK_CUR(1):当前位置
SEEK_END(2):文件末尾
示例:
fseek(fp, 0, SEEK_SET); // 移到文件开头(等效于rewind(fp))
fseek(fp, 10, SEEK_CUR); // 从当前位置向后移10字节
fseek(fp, -5, SEEK_END); // 从文件末尾向前移5字节
5.3 rewind ():重置位置指针
将位置指针移到文件开头,等价于fseek(fp, 0, SEEK_SET)。
rewind(fp); // 指针回到文件开头
六、文件的错误检测
6.1 ferror ():检查文件操作是否出错
若文件操作出错,返回非 0 值;否则返回 0。
if (ferror(fp) != 0) {printf("文件操作错误!");
}
6.2 clearerr ():清除错误标志
重置文件的错误标志和 EOF 标志,避免影响后续操作。
clearerr(fp); // 清除错误状态
七、章节核心要点总结
- 文件操作的基本流程:打开(fopen)→ 读写 → 关闭(fclose),必须判断文件是否成功打开。
- 文本文件与二进制文件的区别:存储形式不同,读写函数不同(文本用
fgetc
/fprintf
等,二进制用fread
/fwrite
)。 - 位置指针:控制读写位置,
fseek
是随机读写的核心函数。 - 安全性:操作完毕必须关闭文件,避免数据丢失或资源泄露。