文章目录
- [1. 核心概念:文件指针 (FILE *)](#1. 核心概念:文件指针 (FILE *))
- [2. 文件的打开与关闭](#2. 文件的打开与关闭)
-
- [2.1 打开文件:fopen()](#2.1 打开文件:fopen())
- [2.2 关闭文件:fclose()](#2.2 关闭文件:fclose())
- [3. 文件的读写操作](#3. 文件的读写操作)
-
- [3.1 字符读写 (Character I/O)](#3.1 字符读写 (Character I/O))
- [3.2 字符串/行读写 (Line I/O)](#3.2 字符串/行读写 (Line I/O))
- [3.3 格式化读写 (Formatted I/O)](#3.3 格式化读写 (Formatted I/O))
- [3.4 块读写 (Binary/Block I/O)](#3.4 块读写 (Binary/Block I/O))
- [4. 文件定位 (Random Access)](#4. 文件定位 (Random Access))
- [5. 错误处理](#5. 错误处理)
- [7. 常见陷阱与最佳实践](#7. 常见陷阱与最佳实践)
C语言的文件读写(File I/O)是C标准库(stdio.h)提供的一套强大功能,用于在程序和外部文件(如txt、dat、csv等)之间传输数据。
以下是C语言文件操作的核心知识体系,包含基本概念、常用函数、操作模式和完整示例。
1. 核心概念:文件指针 (FILE *)
在C语言中,文件被看作是一个字节流。系统通过一个结构体 FILE 来管理文件的状态(如当前读写位置、错误标志、文件缓冲区等)。
我们不直接操作 FILE 结构体,而是通过 文件指针 (FILE *) 来引用它。
标准输入/输出也是文件:stdin(键盘)、stdout(屏幕)、stderr(错误输出)。
2. 文件的打开与关闭
2.1 打开文件:fopen()
在读写文件前,必须先打开文件。
bash
FILE *fopen(const char *filename, const char *mode);
filename: 文件路径(如 "data.txt" 或 "C:\test\data.bin")。
mode: 打开模式(字符串)。
| 模式 | 含义 | 文件不存在时 |
|---|---|---|
| "r" | 只读(文本) | 报错 (NULL) |
| "w" | 只写(文本) | 创建新文件 |
| "a" | 追加(文本) | 创建新文件 |
| "r+" | 读写(文本) | 报错 |
| "w+" | 读写(文本) | 创建新文件(覆盖原内容) |
| "a+" | 读写(追加) | 创建新文件 |
| 二进制模式 (在上述后加 b) | ||
| "rb" | 只读(二进制) | 报错 |
| "wb" | 只写(二进制) | 创建新文件 |
注意:文本模式下,Windows会将 \n 转换为 \r\n,而Linux/Mac不会;二进制模式 (b) 则原样读写,适合图片、音频、结构体。
2.2 关闭文件:fclose()
操作完成后必须关闭文件,以释放资源并将缓冲区数据刷新到磁盘。
bash
int fclose(FILE *stream);
// 成功返回0,失败返回EOF(-1)
3. 文件的读写操作
根据读取数据的单位不同,分为四类:
3.1 字符读写 (Character I/O)
适用于逐个字符处理。
int fgetc(FILE *fp): 读取一个字符,返回其ASCII码,失败或EOF返回 EOF。
int fputc(int c, FILE *fp): 写入一个字符。
bash
// 示例:复制文件(字符版)
char ch;
while ((ch = fgetc(fin)) != EOF) {
fputc(ch, fout);
}
3.2 字符串/行读写 (Line I/O)
适用于读取配置文件、日志等文本。
char *fgets(char *buf, int size, FILE *fp): 读取一行(最多 size-1 个字符),安全(防止溢出),会保留换行符 \n。
int fputs(const char *s, FILE *fp): 写入字符串(不自动加 \n)。
bash
char buffer[100];
if (fgets(buffer, 100, fp) != NULL) {
printf("Read line: %s", buffer);
}
3.3 格式化读写 (Formatted I/O)
用法与 printf / scanf 完全一致,只是针对文件。
int fprintf(FILE *fp, const char *format, ...): 格式化写入。
int fscanf(FILE *fp, const char *format, ...): 格式化读取。
bash
int age = 25;
fprintf(fp, "Name: %s, Age: %d\n", "Alice", age);
3.4 块读写 (Binary/Block I/O)
适用于结构体、数组、图片等二进制数据,效率最高。
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *fp): 从文件读入数据块到内存。
size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *fp): 将内存数据块写入文件。
bash
// 示例:写入结构体
struct Student { int id; char name[20]; };
struct Student s = {1, "Tom"};
fwrite(&s, sizeof(struct Student), 1, fp);
// 示例:读取结构体
fread(&s, sizeof(struct Student), 1, fp);
4. 文件定位 (Random Access)
不一定要顺序读写,可以跳转到文件任意位置。
1、int fseek(FILE *fp, long offset, int whence): 移动文件指针。
Parameter analysis:
whence: SEEK_SET(文件头), SEEK_CUR(当前位置), SEEK_END(文件尾)。
2、long ftell(FILE *fp): 获取当前文件指针位置。
3、void rewind(FILE *fp): 将指针重置到文件开头。
5. 错误处理
文件操作非常容易出错(如磁盘满、无权限、文件被占用),必须检查返回值。
1、fopen失败: 返回 NULL。
2、ferror(FILE *fp): 检查文件流是否发生错误。
3、feof(FILE *fp): 检查是否到达文件末尾(End Of File)。
4、perror(): 打印系统错误信息。
bash
FILE *fp = fopen("nonexist.txt", "r");
if (fp == NULL) {
perror("Error opening file"); // 输出类似: Error opening file: No such file or directory
return 1;
}
7. 常见陷阱与最佳实践
1、忘记关闭文件:会导致数据丢失(数据还在缓冲区没写入磁盘)和资源泄漏。
2、不检查返回值:fopen 可能失败,fread 可能读不满。
3、模式混淆:用 "r" 读二进制文件(如jpg)会出错,因为系统可能对字节 0x1A 做特殊处理(在4、4、DOS/Windows下视为EOF)。读写非纯文本文件务必用 b 模式。
5、路径问题:Windows下路径分隔符是 \(因为 \ 是转义符),或者使用正斜杠 /(C标准库支持)。
6、缓冲区:fopen 默认是全缓冲(除 stderr 外)。数据不会立即写入磁盘,除非缓冲区满、遇到 \n(行缓冲)或调用 fflush() / fclose()。