文件IO与标准IO
核心概念
1. 文件 I/O (Unbuffered I/O / 低级 I/O)
- 定义:文件 I/O 指的是操作系统提供的一组最底层、无缓冲的输入/输出系统调用。它直接与操作系统内核交互,通过文件描述符fd (File Descriptor) 来标识和操作打开的文件。
- 特点:
- 无缓冲:每次读写操作都直接发起系统调用,进入内核态,直接操作磁盘或设备。没有用户空间的缓冲区来减少系统调用次数。
- 文件描述符:使用一个非负整数(如
0, 1, 2, 3...)来代表一个打开的文件。0是标准输入(STDIN_FILENO),1是标准输出(STDOUT_FILENO),2是标准错误(STDERR_FILENO)。 - POSIX 标准:是 UNIX/Linux 系统遵循的一套通用接口。
- 主要函数 (在
<unistd.h>中):open(): 打开或创建一个文件。read(): 从文件描述符读取数据。write(): 向文件描述符写入数据。lseek(): 移动文件指针。close(): 关闭文件描述符。
- 优点:
- 实时性强:数据直接写入设备或文件,没有延迟。
- 控制精细:可以提供更底层的控制,如同步写入(使用
O_SYNC标志)。
- 缺点:
- 效率低:频繁的系统调用(上下文切换:用户态->内核态)会导致较大的性能开销,尤其是进行大量小块数据读写时。
示例:使用文件 I/O 复制文件
#include <fcntl.h>
#include <unistd.h>
int main() {
int src_fd = open("source.txt", O_RDONLY);
int dest_fd = open("dest.txt", O_WRONLY | O_CREAT, 0644);
char buffer[1024];
ssize_t bytes_read;
while ((bytes_read = read(src_fd, buffer, sizeof(buffer))) > 0) {
write(dest_fd, buffer, bytes_read);
}
close(src_fd);
close(dest_fd);
return 0;
}
2. 标准 I/O (Buffered I/O / 高级 I/O)
- 定义:标准 I/O 是 C 语言标准库(如
glibc)提供的一套高级的、带缓冲的输入/输出函数。它构建在文件 I/O 之上,通过维护一个用户空间的缓冲区来优化效率。 - 特点:
- 有缓冲:在用户空间开辟缓冲区。读写操作先与这个缓冲区交互,只有当缓冲区满(输出)或空(输入)时,才调用底层的文件 I/O 函数进行真正的系统调用。
- 文件指针:使用
FILE*类型的指针(如stdin, stdout, stderr)来指向一个文件流(Stream)。 - C 语言标准:是 ANSI C 标准的一部分,跨平台性更好。
- 主要函数 (在
<stdio.h>中):fopen(): 打开一个文件流。fread(),fgets(),fgetc(): 从文件流读取数据。fwrite(),fputs(),fputc(): 向文件流写入数据。fseek(),ftell(): 操作文件流的位置指针。fclose(): 关闭文件流。
- 缓冲策略:
- 全缓冲:通常用于磁盘文件。缓冲区满后才进行实际 I/O 操作。
- 行缓冲:通常用于终端(如
stdout)。遇到换行符\n或缓冲区满时进行实际 I/O 操作。 - 无缓冲:通常用于标准错误
stderr,要求输出立即显现。
- 优点:
- 效率高:通过减少系统调用的次数,大大提高了 I/O 效率,尤其适合小块、频繁的读写。
- 功能强大:提供了更方便的格式化输入输出(如
printf,scanf)、行读写等高级功能。
- 缺点:
- 实时性差:由于缓冲的存在,数据可能不会立即写入设备(除非手动
fflush)。 - 控制不够底层:无法处理某些需要精细控制的场景(如非阻塞 I/O、异步 I/O)。
- 实时性差:由于缓冲的存在,数据可能不会立即写入设备(除非手动
示例:使用标准 I/O 复制文件
#include <stdio.h>
int main() {
FILE *src_file = fopen("source.txt", "r");
FILE *dest_file = fopen("dest.txt", "w");
char buffer[1024];
size_t bytes_read;
while ((bytes_read = fread(buffer, 1, sizeof(buffer), src_file)) > 0) {
fwrite(buffer, 1, bytes_read, dest_file);
}
fclose(src_file);
fclose(dest_file);
return 0;
}
核心区别与总结
| 特性 | 文件 I/O (Unbuffered I/O) | 标准 I/O (Buffered I/O) |
|---|---|---|
| 所属层面 | 操作系统层 (系统调用) | C 语言运行时库层 (库函数) |
| 标识符 | 文件描述符 (int, 如 fd) | 文件指针 (FILE*, 如 fp) |
| 缓冲 | 无缓冲,每次操作都是系统调用 | 有缓冲,减少系统调用,效率高 |
| 标准 | POSIX 标准 (UNIX/Linux) | ANSI C 标准 (跨平台) |
| 函数举例 | open, read, write, close | fopen, fread, fwrite, fclose |
| 优点 | 实时性强,控制精细 | 效率高,功能强大,使用方便 |
| 缺点 | 效率低,系统调用开销大 | 实时性差,控制不够底层 |
| 适用场景 | 设备文件、管道、网络套接字、需要原子操作或同步写入的场景 | 绝大多数普通的磁盘文件读写操作 |

联系与转换
- 底层实现:标准 I/O 库的内部最终是通过调用文件 I/O(如
read,write)来实现实际的数据传输的。FILE结构体内部就包含了一个文件描述符。 - 相互转换:
-
从文件描述符到文件流:可以使用
fdopen()函数将一个已打开的文件描述符包装成一个FILE*文件流。int fd = open("file.txt", O_RDWR); FILE *fp = fdopen(fd, "r+"); // 现在可以用fscanf, fprintf等操作fp了 -
从文件流到文件描述符:可以使用
fileno()函数从一个文件流获取其底层的文件描述符。FILE *fp = fopen("file.txt", "r"); int fd = fileno(fp); // 现在可以用read, write等操作fd了
-
如何选择?
- 对于普通文件操作:文本文件优先使用标准 I/O。它的缓冲机制能带来巨大的性能提升,而且API更易用、更安全。
- 在以下情况下使用文件 I/O:适用于对二进制文件进行读写,以及对底层设备的访问,如网络套接字(socket)、硬件设备等。因为无缓冲机制,所以适用于实时性要求高的场景(比如传感器数据的采集)
简单来说,标准 I/O 是处理大多数问题的首选工具,而文件 I/O 则是当你需要钻到更底层进行精细控制时的备用工具。

4286

被折叠的 条评论
为什么被折叠?



