Linux 软件编程 - IO 编程
一、IO 核心概念:理解 “一切皆文件”
在 Linux 中,IO 操作的本质是对 “文件” 的操作 —— 这里的 “文件” 不仅包括我们日常接触的文本 / 二进制文件,还涵盖了设备(键盘、鼠标、磁盘)、通信对象(管道、套接字)等。所有这些 “文件” 都通过统一的文件描述符或流进行管理,实现了 “屏蔽底层差异,统一操作接口” 的目标。
1.1 常见文件类型分类
不同类型的 “文件” 对应不同的 IO 场景,其标识和用途如下表所示:
| 文件标识 | 类型名称 | 核心用途 | 典型示例 | |
|---|---|---|---|---|
| b | 块设备文件 | 按 “块” 读写,用于存储设备 | 硬盘、U 盘、分区 | |
| c | 字符设备文件 | 按 “字符” 读写,用于交互设备 | 键盘、鼠标、终端(tty) | |
| d | 目录文件 | 存储文件 / 子目录的索引信息 | /home、/etc 目录 | |
| - | 普通文件 | 存储用户数据 | 文本文件(.txt)、二进制文件(.exe) | |
| l | 链接文件 | 指向其他文件的 “快捷方式” | 软链接(ln -s 创建) | |
| s | 套接字文件 | 进程间网络通信 | 网络服务端 / 客户端通信 | |
| p | 管道文件 | 本地进程间通信 | 匿名管道( | )、命名管道 |
二、IO 接口分类:按需选择合适的操作方式
根据操作对象的不同,Linux IO 接口分为三类,核心差异在于是否有缓存和适用场景:
| 接口类型 | 操作对象 | 缓存特性 | 核心用途 |
|---|---|---|---|
| 标准 IO | 普通文件(文本 / 二进制) | 有缓存(高效) | 日常文件读写(如配置文件、日志) |
| 文件 IO | 设备文件、通信文件(管道) | 无缓存(实时) | 硬件操作、实时数据交互 |
| 目录 IO | 目录文件 | 无缓存 | 目录创建、删除、遍历(如 ls 功能) |
三、标准 IO 详解:从基础到核心接口
标准 IO 由 C 标准库()提供,基于 “流(FILE*)” 实现对文件的操作,支持缓存管理、格式化读写等功能,适用于绝大多数普通文件场景。
3.1 标准 IO 的核心前提
1. 头文件
使用标准 IO 必须包含头文件:
#include
2. 普通文件的两种形式
- ASCII 码文件:内容由可显示的 ASCII 字符组成(如代码、文本),可通过
cat直接查看; - 二进制文件:内容是二进制数据(如图片、音视频、压缩包),直接查看会显示乱码;
- 注意:ASCII 码文件是特殊的二进制文件(仅包含 0~127 的 ASCII 值)。
3. 默认打开的 3 个流
程序启动时,系统会自动打开 3 个标准流,无需手动fopen:
stdin:标准输入流(对应键盘),行缓存;stdout:标准输出流(对应终端),行缓存;stderr:标准错误流(对应终端),无缓存(错误信息需实时输出)。
3.2 标准 IO 的缓存机制
缓存是标准 IO 的核心优化 —— 通过在内存中开辟缓存区,批量读写数据,减少与磁盘 / 终端的直接交互次数。标准 IO 的缓存分为三类:
| 缓存类型 | 缓存大小 | 刷新条件(数据写入目标) | 适用场景 |
|---|---|---|---|
| 全缓存 | 4KB(默认) | 1. 缓存区满;2. 调用fclose/ 程序结束;3. 手动fflush | 普通文件(如.log) |
| 行缓存 | 1KB(默认) | 1. 缓存区满;2. 遇到
;3. 调用fclose/ 程序结束;4. 手动fflush | 终端交互(stdin/stdout) |
| 不缓存 | 0KB | 无缓存,数据直接写入目标 | 错误输出(stderr) |
示例:用printf("Hello")打印时,若未加
,stdout(行缓存)不会立即输出;加上
或调用fflush(stdout),数据才会显示到终端。
3.3 标准 IO 核心函数接口
标准 IO 提供了一套完整的文件操作函数,从 “打开 - 读写 - 关闭” 到 “定位 - 刷新”,覆盖所有常见场景。以下是最常用的函数详解:
1. 文件打开:fopen
- 原型:
FILE *fopen(const char *pathname, const char *mode); - 功能:打开指定路径的文件,并创建一个 “流(FILE*)” 用于后续操作;
- 参数:
pathname:文件路径(如./test.txt、/home/user/data.bin);mode:打开模式(决定读写权限和文件不存在时的行为),常见模式如下:
| mode | 读写权限 | 文件不存在时 | 文件存在时 | 适用场景 |
|---|---|---|---|---|
| r | 只读 | 报错(NULL) | 打开文件 | 读取配置文件 |
| r+ | 读写 | 报错(NULL) | 打开文件(不清空) | 读写已存在的文件 |
| w | 只写 | 创建文件 | 清空文件内容 | 新建日志文件(覆盖旧内容) |
| w+ | 读写 | 创建文件 | 清空文件内容 | 新建并读写文件 |
| a | 追加只写 | 创建文件 | 指针定位到文件尾 | 追加日志(不覆盖旧内容) |
| a+ | 追加读写 | 创建文件 | 指针定位到文件尾 | 追加并读取日志 |
- 返回值:成功返回
FILE*流指针;失败返回NULL(需用perror查看错误原因)。
2. 文件关闭:fclose
- 原型:
int fclose(FILE *stream); - 功能:关闭流,释放缓存和文件资源(必须调用,避免内存泄漏和数据丢失);
- 参数:
stream:fopen返回的流指针; - 返回值:成功返回 0;失败返回
EOF(-1)。
3. 字符读写:fgetc / fputc
适用于逐字符读写(如统计文件字符数)。
-
fgetc(读字符):- 原型:
int fgetc(FILE *stream); - 功能:从流中读取一个字符(返回 ASCII 码值);
- 返回值:成功返回字符 ASCII 码;失败 / 文件末尾返回
EOF(-1); - 等价:
getchar()≡fgetc(stdin)(从键盘读字符)。
- 原型:
-
fputc(写字符):- 原型:
int fputc(int c, FILE *stream); - 功能:将字符
c(ASCII 码)写入流; - 返回值:成功返回字符
c;失败返回EOF; - 等价:
putchar(c)≡fputc(c, stdout)(向终端写字符)。
- 原型:
4. 字符串读写:fgets / fputs
适用于逐行读写(如读取配置文件的行内容)。
-
fgets(读字符串):- 原型:
char *fgets(char *s, int size, FILE *stream); - 功能:从流中读取最多
size-1个字符(留 1 个位置存
- 原型:

