第9章 I/O库函数——教材知识点归纳
9.1~9.2 系统调用和I/O库函数
-
系统调用:操作系统中,进程以两种不同的方式运行:内核模式(Kmode)和用户模式(Umode)。Umode权限有限,特殊权限的操作需要在Kmode下进行。系统调用(System Call)机制允许进程进入Kmode,执行更高权限的操作。
-
系统调用和I/O库函数不同,但I/O库函数建立在系统调用的基础上。例如,fopen()依赖于open(),fread()依赖于read()。
-
常见的系统调用函数包括:open()、read()、write()、lseek()、close()。
9.3 I/O库函数的算法
9.3.1 fread算法
-
在第一次调用fread()时,FILE结构体的缓冲区是空的,fread()使用保存的文件描述符fd发出一个系统调用:
n=read(fd, fbuffer, BLKSIZE)
,用数据块填充内部的fbuf[]。然后,它会初始化fbuf[]的指针、计数器和状态变量,以表明内部缓冲区中有一个数据块。 -
在随后的每次fread()调用中,它都尝试满足来自FILE结构体内部缓冲区的调用。当缓冲区变为空时,它就会发出read()系统调用来重新填充内部缓冲区。
9.3.2 fwrite()算法
- fwrite()算法与fread()算法相似,只是数据传输方向不同。开始时,FILE结构体的内部缓冲区是空的。在每次调用fwrite()时,它将数据写入内部缓冲区,并调整缓冲区的指针、计数器和状态变量,以跟踪缓冲区中的字节数。如果缓冲区已满,则发出write()系统调用,将整个缓冲区写入操作系统内核。
9.3.3 fclose()算法
- 若文件以写的方式被打开,fclose()会先关闭文件流的局部缓冲区。然后,它会发出一个close(fd)系统调用来关闭FILE结构体中的文件描述符。最后,它会释放FILE结构体,并将FILE指针重置为NULL。
9.4~9.5 使用I/O库函数、I/O库模式
-
对于以BLKSIZE为单位的读/写数据,使用系统调用比I/O库函数更高效。
-
I/O库模式在fopen()中体现,包括字符模式和二进制模式:
-
字符模式:
- r:只读模式,文件必须存在,否则打开失败。
- w:只写模式,若文件存在,则清除原文件内容后写入;否则,新建文件后写入。
- a:追加模式,若文件存在,则位置指针移到文件末尾,在文件尾部追加写入。若文件不存在,则打开失败。
- r+:表示读/写,不会截断文件。
- w+:表示读/写,但是先截断文件,如果文件不存在,就先创建文件。
- a+:表示通过追加的方式读/写;如果文件不存在就创建文件。
-
二进制模式:
- rb:只读模式,文件必须存在,否则打开失败。
- wb:只写模式,若文件存在,则清除原文件内容后写入;否则,新建文件后写入。
- ab:追加模式,若文件存在,则位置指针移到文件末尾,在文件尾部追加写入。若文件不存在,则打开失败。
- rb+:表示读/写,不会截断文件。
- wb+:表示读/写,但是先截断文件,如果文件不存在,就先创建文件。
- ab+:表示通过追加的方式读/写;如果文件不存在就创建文件。
-
-
字符模式I/O:
- 字符模式I/O以字符为单位存取,文件在流中。
- 从文件指针中读取一个字符:
int fgetc(FILE *fp)
。 - 向文件流中退回一个字符:
int ungetc(int c, FILE *fp)
。 - 向文件流中存放一个字符:
int fputc(int c, FILE *fp)
。 - 注意读取到文件结束时,会返回值-1,可与文件流中其他字符区分。同时返回值的类型是整数。
-
行模式I/O:
- 行模式I/O以文本文件的行为单位进行存取。
- 读取一行:
char *fgets(char *buf, int size, FILE *fp)
。 - 写入一行:
int fputs(char *buf, FILE *fp)
。
-
格式化I/O:
- 格式化输入和格式化输出就是C语言中常用的scan和print类型。
- scanf和printf指定输入输出流为stdin和stdout,而fscanf和fprintf可以指定输入流和输出流。
- 格式化输入:从stdin输入:
scanf(char *FMT, &items)
;从指定流输入:fscanf(fp, char *FMT, &items)
。 - 格式化输出:输出到stdout:
printf(char *FMT, &items)
;输出到指定流:fprintf(fp, char *FMT, &items)
。
9.6 文件流缓冲
-
每个文件流都有一个FILE结构体,其中包含一个内部缓冲区,对文件流进行读写需要经过FILE缓冲区。
-
文件缓冲可以使用三种缓冲方案之一:
- 无缓冲:尽快单独传输,例如,stderr通
常无缓冲。
- 行缓冲:遇到换行符时进行缓冲,逐行输入输出,例如,stdout。
- 全缓冲:以块大小传输到文件或从文件传输,文件流常用的是全缓冲。
9.7 变参函数
-
在学习C语言时,我们发现printf、scanf函数在传入参数时,参数的个数是不确定的。后来发现,原来它们都是变参函数。
-
变参函数使用至少一个参数声明,后面跟三个点。例如:
int function(int m, int n, ...);
-
在函数内部,可以通过C语言库宏访问这些参数。
实践
-
通过I/O库函数进行简单的操作时,有以下几个基本的操作:
-
打开文件:使用
fopen
函数以指定的模式打开文件。例如,以只读模式打开文件可以这样:FILE *file = fopen("example.txt", "r");
-
读取文件:使用
fread
或fgets
等函数来读取文件内容。例如,使用fgets
可以逐行读取文本文件:char buffer[100]; fgets(buffer, sizeof(buffer), file);
-
写入文件:使用
fwrite
或fputs
等函数来将数据写入文件。例如,使用fprintf
可以格式化写入数据:fprintf(file, "This is a line of text\n");
-
关闭文件:使用
fclose
函数关闭文件,确保在操作完成后关闭文件以释放资源:fclose(file);
-
错误处理:始终检查文件操作的返回值,以便捕获任何可能的错误。你可以使用
feof
和ferror
来检查文件的结束和错误状态。 -
示例
#include <stdio.h>
int main() {
FILE *sourceFile, *destinationFile;
char ch;
// 打开源文件以读取内容
sourceFile = fopen("source.txt", "r");
if (sourceFile == NULL) {
perror("Error opening source file");
return 1;
}
// 打开目标文件以写入内容
destinationFile = fopen("destination.txt", "w");
if (destinationFile == NULL) {
perror("Error opening destination file");
fclose(sourceFile);
return 1;
}
// 从源文件读取字符并写入目标文件
while ((ch = fgetc(sourceFile)) != EOF) {
fputc(ch, destinationFile);
}
// 关闭文件
fclose(sourceFile);
fclose(destinationFile);
printf("File copied successfully.\n");
return 0;
}
- 在这个示例中,程序首先打开了一个源文件("source.txt")以读取内容,然后打开一个目标文件("destination.txt")以写入内容。接着,它使用fgetc从源文件逐字符读取,并使用fputc将字符写入目标文件,直到源文件的末尾(EOF)。最后,程序关闭了两个文件,并输出一条成功的消息。