学习笔记2
9.1~9.2 系统调用和I/O库函数
在操作系统中,进程以两种不同的方式运行:内核模式(Kmode)和用户模式(Umode)。用户模式的权限有限,某些特殊权限的操作需要在内核模式下进行。系统调用(System Call)机制允许进程进入内核模式,执行更高权限的操作。
系统调用和I/O库函数不同,但I/O库函数是基于系统调用构建的。例如,许多I/O库函数依赖于底层的系统调用,如fopen()依赖于open(),fread()依赖于read()。
常见系统调用及功能
-
open():用于打开文件或创建新文件。可以指定文件名、访问模式(读、写、执行等)、文件权限等参数。返回一个文件描述符,用于后续文件操作。
-
read():用于从已打开的文件中读取数据。需要提供文件描述符、读取位置和读取字节数等参数。
-
write():用于向已打开的文件中写入数据。需要提供文件描述符、写入位置和写入字节数等参数。
-
lseek():用于移动文件读写指针的位置。可以在文件中随机访问数据。
-
close():用于关闭已打开的文件,释放文件描述符以及与文件相关的资源。
这些系统调用提供了对文件操作的底层支持。在其上,I/O库函数被构建,以提供更高级别的文件操作接口,使程序员更容易使用这些系统调用来进行文件处理。
例如,C标准库的文件函数,如fopen()
,fread()
,fwrite()
,以及fclose()
,都是建立在系统调用的基础上的。fopen()
函数实际上使用open()
系统调用来打开文件,而fread()
和fwrite()
则使用read()
和write()
系统调用来进行数据读写操作。
通过系统调用,用户程序可以与操作系统内核进行通信,并获得对底层资源的访问权限,这是编写系统级应用程序和操作系统的关键部分。
9.3 I/O库函数的算法
I/O库函数的算法主要包括fread算法、fwrite算法、以及fclose算法。
9.3.1 fread算法
1. 第一次调用fread()
- 在第一次调用fread()时,FILE结构体的缓冲区是空的。
- fread()使用保存的文件描述符fd发出一个
n = read(fd, fbuffer, BLKSIZE)
系统调用,用数据块填充内部的fbuf[]。 - 然后,它会初始化fbuf[]的指针、计数器和状态变量,以表明内部缓冲区中有一个数据块。
2. 随后的fread()调用
- 在随后的每次fread()调用中,它都尝试满足来自FILE结构体内部缓冲区的调用。
- 当缓冲区变为空时,它就会再次发出read()系统调用来重新填充内部缓冲区。
9.3.2 fwrite()算法
- fwrite()算法与fread()算法相似,不同之处在于数据传输方向不同。
- 开始的时候,FILE结构体的内部缓冲区是空的。
- 在每次调用fwrite()时,它将数据写入内部缓冲区,并调整缓冲区的指针、计数器和状态变量,以跟踪缓冲区中的字节数。
- 如果缓冲区已满,则发出write()系统调用,将整个缓冲区写入操作系统内核。
9.3.3 fclose()算法
- 若文件以写的方式被打开,fclose()会先关闭文件流的局部缓冲区。
- 然后,它会发出一个close(fd)系统调用来关闭FILE结构体中的文件描述符。
- 最后,它会释放FILE结构体,并将FILE指针重置为NULL。
这些算法描述了I/O库函数在内部如何操作,以及它们如何与底层系统调用交互。这对于理解文件读写的底层机制非常重要,尤其是在编写需要高效I/O操作的程序时。
9.4~9.5 使用I/O库函数、I/O库模式
对于以BLKSIZE为单位的读/写数据,使用系统调用比I/O库函数更高效。
I/O库模式中的字符模式
r
:只读模式,文件必须存在,否则打开失败。w
:只写模式,若文件存在,则清除原文件内容后写入;否则,新建文件后写入。a
:追加模式。若文件存在,则位置指针移到文件末尾,在文件尾部追加写入。若文件不存在,则打开失败。r+
:表示读/写,不会截断文件。w+
:表示读/写,但是先截断文件,如果文件不存在,就先创建文件。a+
:表示通过追加的方式读/写;如果文件不存在就创建文件。
I/O库模式中的二进制模式
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语言课常用的scanf和printf类型,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操作时,常常会面临各种问题,如文件不存在、权限问题、编码错误、内存消耗过大等。
文件不存在问题:
问题描述: 尝试打开或读取一个不存在的文件,导致FileNotFoundError。
解决方式:
-
在打开文件前,确保文件存在,可以使用os.path.exists()来检查。
-
使用try...except块来捕获FileNotFoundError并进行适当的错误处理。
import os
file_path = "example.txt"
if os.path.exists(file_path):
with open(file_path, 'r') as file:
content = file.read()
else:
print(f"File '{file_path}' does not exist.")
实践过程
创建并写入文件:
#include <stdio.h>
int main() {
// 创建文件并打开以写入模式
FILE *file = fopen("pro.txt", "w");
// 检查文件是否成功打开
if (file == NULL) {
perror("Error opening file");
return 1;
}
// 写入数据到文件
fprintf(file, "Hello, world!\n");
fprintf(file, "C program\n");
// 关闭文件
fclose(file);
printf("已经成功写入\n");
return 0;
}
读取文件内容:
#include <stdio.h>
int main() {
// 打开文件以读取模式
FILE *file = fopen("pro.txt", "r");
// 检查文件是否成功打开
if (file == NULL) {
perror("Error opening file");
return 1;
}
// 读取文件内容并打印
char buffer[100]; // 缓冲区
while (fgets(buffer, sizeof(buffer), file) != NULL) {
printf("%s", buffer); // 不打印多余的换行符
}
// 关闭文件
fclose(file);
return 0;
}
3. 追加内容到文件:
#include <stdio.h>
int main() {
// 打开文件以追加模式
FILE *file = fopen("pro.txt", "a");
// 检查文件是否成功打开
if (file == NULL) {
perror("Error opening file");
return 1;
}
// 写入数据到文件
fprintf(file, "This line is appended.\n");
printf("print successfully");
// 关闭文件
fclose(file);
return 0;
}
posted on 2023-09-17 16:19 20211406张顺扬 阅读(14) 评论(0) 编辑 收藏 举报
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律