Linux 标准输入输出与缓冲机制(标准 IO)
在上一节中,你学习了使用系统调用(如 open()
、read()
、write()
)进行文件 IO 的方式,这种方式直接和操作系统交互,效率高但使用起来略显繁琐。本节我们将深入介绍另一个你更常用的接口 —— 标准输入输出(Standard I/O),也就是你熟悉的 printf()
、scanf()
、fopen()
、fread()
等函数。
你还将了解标准 IO 背后的缓冲机制,以及它和系统调用层的文件 IO 的区别与联系,帮助你在实际开发中根据场景合理选择。
什么是标准输入输出
标准输入输出(Standard I/O,简称“标准 IO”)是 C 标准库(stdio.h
)提供的一组高层接口,它对系统调用进行了封装,使用更简单,适合大多数通用程序开发。
常用标准 IO 函数包括:
- 文件操作:
fopen()
、fclose()
、fread()
、fwrite()
、fprintf()
、fscanf()
; - 输入输出:
printf()
、scanf()
、putchar()
、getchar()
; - 文件定位:
fseek()
、ftell()
; - 缓冲控制:
fflush()
、setbuf()
。
标准 IO 操作的是 文件指针(FILE *
),而不是文件描述符。
标准 IO 文件操作函数
创建/打开文件
每当你想要处理一个文件时,第一步是创建文件。文件本质上是在内存中用于存储数据的一块空间。
在 C 程序中创建文件的语法如下:
FILE *fp;
fp = fopen("file_name", "mode");
在上述语法中,
FILE
是在标准库中定义的数据结构,fp
是指向文件的指针。fopen()
是用于打开文件的标准函数。- 如果文件在系统中不存在,则创建该文件并打开。(返回
FILE*
类型) - 如果文件已存在于系统中,则直接打开该文件。(失败返回
NULL
)
- 如果文件在系统中不存在,则创建该文件并打开。(返回
mode
模式表示文件的打开方式,例如"r"
表示以只读方式打开、"w"
表示写入并清空原文件、"a"
表示追加写入、"rb"
表示以二进制读等。
每当你打开或创建一个文件时,必须指定你打算对文件进行的操作。下表列出了 fopen()
函数支持的所有文件操作模式:
文件模式 | 描述 |
---|---|
r | 以读取方式打开文件。如果文件存在,打开文件并从文件开头读取内容。 |
w | 以写入方式打开文件。如果文件不存在,创建新文件;如果文件存在,清空 文件内容。 |
a | 以追加方式打开文件。如果文件不存在,创建新文件;如果文件存在,写入的数据将被追加到文件末尾。 |
r+ | 以读写方式打开文件,文件指针指向文件开头。 |
w+ | 以读写方式打开文件,如果文件存在,清空文件内容;如果文件不存在,创建新文件。 |
a+ | 以追加读写方式打开文件,如果文件存在,文件指针指向文件末尾;如果文件不存在,创建新文件。 |
另外,还有 rb
/ wb
/ ab
/ r+b
/ w+b
/ a+b
模式,它们是以二进制模式打开文件,而不是文本格式。模式功能与上面类似,只是操作的是二进制数据。
在给定的语法中,文件名和模式都作为字符串指定,因此必须用双引号括起来。
示例:
#include <stdio.h>
int main() {
FILE *fp;
fp = fopen("data.txt", "w");
return 0;
}
编译执行程序,你将看到当前目录下创建了一个 data.txt 文件。
你也可以指定创建文件的路径:
#include <stdio.h>
int main() {
FILE *fp;
fp = fopen("~/data.txt", "w"); // Windows 风格路径:"D://data.txt"
return 0;
}
关闭文件
在完成对文件的操作后,应该始终关闭文件。这意味着终止对文件的内容和链接,从而防止文件的意外损坏。
C 语言提供了 fclose
函数来关闭文件。
fclose(fp);
关闭文件后,文件指针 fp
不再指向任何文件。
FILE *fp;
fp = fopen ("data.txt", "r");
fclose (fp);
fclose()
函数接受一个文件指针作为参数。然后,它会关闭与该文件指针关联的文件。如果关闭成功,则返回 0;如果关闭文件时发生错误,则返回 EOF
(文件结束)。
关闭文件后,相同的文件指针还可以用于其他文件。
在 C 语言编程中,虽然程序终止时文件会自动关闭。但使用 fclose()
函数手动关闭文件是一种很好的编程习惯。
写入文件
Linux 标准 IO 库提供了几个写入文件所需的函数,包括:
fwrite(buffer, size, nmemb, file_pointer)
:将buffer
缓冲区中的内容按指定大小写入file_pointer
指向的文件,支持写入文本和二进制格式数据。fputc(char, file_pointer)
:将一个 字符写入file_pointer
指向的文件。fputs(str, file_pointer)
:将字符串写入file_pointer
指向的文件。fprintf(file_pointer, str, variable_lists)
:将字符串打印到file_pointer
指向的文件中。该字符串可以包含格式说明符和变量列表(variable_lists
),因此可以很好地控制写入的文本格式。
后面三个函数操作的是字符或字符串,尤其是 fputs
和 fprintf
函数,当你写入文件时,必须明确添加换行符(\n
),否则数据可能不会及时写入。
下面通过代码演示这四个文件写入函数的使用。
示例 1:fwrite() 函数
#include <stdio.h>
#include <string.h>
int main() {
FILE *fptr = fopen("data.bin", "wb");
char buffer[] = "Hello, Linux! From GetIoT.tech.\n";
fwrite(buffer, sizeof(char), strlen(buffer), fptr);
fclose(fptr);
return 0;
}
说明:
- 在上面的程序中,我们以
wb
二进制写入模式创建并打开了一个名为 data.txt 的文件。 - 调用
fwrite()
函数执行写入操作,将 buffer 中的数据写入文件。
示例 2:fputc() 函数
#include <stdio.h>
int main()
{
int i;
FILE * fptr;
char fn[50];
char str[] = "Hello, Linux! From GetIoT.tech.\n";
fptr = fopen("data.txt", "w"); // "w" defines "writing mode"
for (i = 0; str[i] != '\n'; i++) {
/* write to file using fputc() function */
fputc(str[i], fptr);
}
fclose(fptr);
return 0;
}
上述程序将单个字符写入 data.txt 文件,直到遇到下一行符号 \n
,表示该句子已成功写入。该过程是取出数组中的每个字符并将其写入文件。
- 首先以
w
写入模式创建并打开了一个名为 data.txt 的文件,并声明将写入文件的字符串。 - 由于
fputc
每次只写入一个字符,因此你需要使用for
循环遍历整个字符串。
示例 3:fputs() 函数
#include <stdio.h>
int main()
{
FILE * fp;
fp = fopen("data.txt", "w+");
fputs("Hello, Linux! ", fp);
fputs("From GetIoT.tech.\n", fp);
fclose(fp);
return (0);
}
说明:
- 在上面的程序中,我们以写入模式创建并打开了一个名为 data.txt 的文件。
- 调用
fputs()
函数执行写入操作,写入两个不同的字符串。