一、标准 IO 的起源与概念
标准 IO(Standard Input/Output)是由 Dennis Ritchie 在 1975 年设计的一套 IO 库,后来成为 C 语言的标准组成部分,并被 ANSI C 所采纳。它是对底层文件 IO 的封装,提供了更便捷、可移植的文件操作接口。
核心特点:
- 设备抽象:将输入输出设备抽象为文件操作
- 标准输入设备:默认是键盘(
/dev/input) - 标准输出设备:默认是显示器
- 标准输入设备:默认是键盘(
- 跨平台性:任何支持标准 C 的系统都可使用
- 缓冲机制:在用户程序和文件 IO 之间加入缓冲区,减少系统调用次数,提高效率
在 Linux 系统中,一切皆文件,IO 操作本质上都是对文件的操作。标准 IO 作为 C 语言标准库的一部分,为文件操作提供了统一接口。
二、文件的基本概念
1. 文件的作用
文件是 Linux 系统中存储数据(包括普通数据和指令)的基本单位,是数据持久化的载体。
2. Linux 文件类型(7 种)
d:目录文件-:普通文件l:链接文件(link)p:管道文件(pipe)s:套接字文件(socket)c:字符设备文件b:块设备文件
可通过ls -l命令查看文件类型,第一个字符即表示文件类型。
3. 文件内容分类
- 文本文件:由 ASCII 字符组成,可直接阅读
- 二进制文件:由二进制数据组成,通常需要特定程序解析
三、IO 的分类
1. 标准 IO
- 是 ANSI C 设计的一组封装了文件 IO 的库函数
- 头文件:
stdio.h(位于/usr/include/stdio.h) - 实现方式:
stdio.h→stdio.c→libc.so(动态库,位于/usr/lib) - 特点:带缓冲机制,可移植性好
2. 文件 IO
- 属于系统调用,是底层操作接口
- 特点:无缓冲,直接与内核交互,效率高但使用复杂
四、头文件引用方式
-
#include <stdio.h>:引用系统库函数头文件- 搜索路径:系统默认路径(如
/usr/include/)
- 搜索路径:系统默认路径(如
-
#include "xxx.h":引用用户自定义头文件- 搜索路径:当前目录
五、流(Stream)的概念
流是数据从文件流入和流出所体现的字节序列,在标准 IO 中用FILE*指针表示(称为流对象或文件流指针)。
1. 流的分类
- 二进制流:由二进制数据组成的流
- 文本流:由 ASCII 码数据组成的流
2. 标准流
C 语言默认打开三个标准流:
stdin:标准输入流(对应键盘)stdout:标准输出流(对应显示器)stderr:标准错误流(对应显示器)
六、缓冲区机制
标准 IO 的一大特点是采用缓冲机制,目的是减少系统调用,提高效率。
1. 缓冲区类型
-
行缓冲(1KB)
- 应用场景:主要用于终端交互(如
stdout) - 刷新条件:遇到
\n、缓冲区满、程序正常结束、fflush()强制刷新
- 应用场景:主要用于终端交互(如
-
全缓冲(4KB)
- 应用场景:主要用于普通文件操作
- 刷新条件:缓冲区满、程序正常结束、
fflush()强制刷新
-
无缓冲(0KB)
- 应用场景:主要用于错误处理(如
stderr) - 特点:数据直接输出,不经过缓冲
- 应用场景:主要用于错误处理(如
2. 缓冲区操作函数
fflush(FILE *stream):强制刷新缓冲区
七、标准 IO 常用函数
1. 文件打开与关闭
-
fopen():打开文件并建立流FILE *fopen(const char *path, const char *mode);mode参数:r:只读(文件不存在则报错)r+:读写(文件不存在则报错)w:只写(文件不存在则创建,存在则清空)w+:读写(文件不存在则创建,存在则清空)a:追加写(文件不存在则创建)a+:追加读写(文件不存在则创建)
-
fclose():关闭文件流int fclose(FILE *stream);
2. 字符操作函数
fgetc(FILE *stream):从流中读取一个字符fputc(int c, FILE *stream):向流中写入一个字符getchar():从标准输入读取一个字符(fgetc(stdin)的宏定义)putchar(int c):向标准输出写入一个字符(fputc(c, stdout)的宏定义)
示例:实现简单的输入输出循环
while(1)fputc(fgetc(stdin), stdout);
3. 行操作函数
-
fgets(char *s, int size, FILE *stream):从流中读取一行数据char *fgets(char *s, int size, FILE *stream);- 最多读取
size-1个字符,自动添加\0 - 遇到
\n会停止读取,且\n会被包含在结果中
- 最多读取
-
fputs(const char *s, FILE *stream):向流中写入一行数据int fputs(const char *s, FILE *stream); -
gets()/puts():与fgets()/fputs()类似,但gets()没有缓冲区大小限制,存在安全隐患
4. 块操作函数(二进制操作)
-
fread():从流中读取指定大小的数据块size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream); -
fwrite():向流中写入指定大小的数据块size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);
示例:结构体读写
struct person {char name[20];int age;char sex;char addr[100];
};struct person p = {"Tom", 20, 'M', "Beijing"};
fwrite(&p, sizeof(struct person), 1, fp);
5. 文件定位函数
-
fseek():移动文件指针int fseek(FILE *stream, long offset, int whence);whence参数:SEEK_SET:从文件开头SEEK_CUR:从当前位置SEEK_END:从文件末尾
-
ftell():获取当前文件指针位置long ftell(FILE *stream); -
rewind():将文件指针移到开头(等效于fseek(stream, 0L, SEEK_SET))void rewind(FILE *stream);
6. 其他常用函数
printf()/scanf():格式化输入输出sprintf():将格式化数据写入字符串feof():判断文件是否到达末尾ferror():检测流是否出错clearerr():清除流出错标记
八、标准 IO 操作流程
- 打开文件:使用
fopen()获取文件流指针(FILE*) - 读写操作:根据需求选择合适的 IO 函数(字符、行、块操作)
- 关闭文件:使用
fclose()关闭文件流,释放资源
九、实践练习
1. 实现简易cat程序
#include <stdio.h>int main(int argc, char *argv[]) {FILE *fp;int c;if (argc != 2) {fprintf(stderr, "Usage: %s filename\n", argv[0]);return 1;}fp = fopen(argv[1], "r");if (fp == NULL) {fprintf(stderr, "Cannot open file %s\n", argv[1]);return 1;}while ((c = fgetc(fp)) != EOF) {fputc(c, stdout);}fclose(fp);return 0;
}
2. 实现文件拷贝功能
#include <stdio.h>
#include <stdlib.h>#define BUFFER_SIZE 1024int main(int argc, char *argv[]) {FILE *src, *dest;char buffer[BUFFER_SIZE];size_t n;if (argc != 3) {fprintf(stderr, "Usage: %s source destination\n", argv[0]);return 1;}src = fopen(argv[1], "rb");if (src == NULL) {fprintf(stderr, "Cannot open source file %s\n", argv[1]);return 1;}dest = fopen(argv[2], "wb");if (dest == NULL) {fprintf(stderr, "Cannot open destination file %s\n", argv[2]);fclose(src);return 1;}while ((n = fread(buffer, 1, BUFFER_SIZE, src)) > 0) {fwrite(buffer, 1, n, dest);}fclose(src);fclose(dest);return 0;
}
十、man 手册的使用
man 手册是查询函数和命令的重要工具,分为不同章节:
man 1 xxx:查看命令帮助man 2 xxx:查看系统调用函数man 3 xxx:查看标准库函数
例如:
man 3 fopen:查看 fopen 函数的详细说明man 3 printf:查看 printf 函数的用法
总结
标准 IO 是 C 语言中处理文件操作的重要接口,通过缓冲机制提高了 IO 效率,同时提供了丰富的函数族满足不同场景的需求。掌握标准 IO 的使用,对于 C 语言程序开发至关重要。从基本的字符读写到复杂的文件定位,标准 IO 都提供了简洁而强大的解决方案,是每个 C 程序员必须掌握的基础知识。