20201322陈俊池学习笔记2
第九章 I/O库函数
一、知识点归纳
9.1 I/O库函数
系统调用是文件操作的基础,但它们只支持数据块的读/写。 实际上,用户程序可能希望以最适合应用程序的逻辑单元读/写文件,如行、字符、结构化记录等,而系统调用不支持这些逻辑单元。I/O库函数实现了这一目的。
9.2 I/O库函数与系统调用
-
系统调用函数:open()、read()、write()、lseek()、close();
-
I/O库函数:fopen()、fread()、fwrite()、fseek()、fclose()。 fopen()依赖于open(),fread()依赖于read()。
9.3 I/O库函数的算法
I/O库函数的算法分为fread算法、fwrite算法、fclose算法。
9.3.1 fread算法
(1) 在第一次调用fread()时,FILE结构体的缓冲区是空的,fread()使用保存的文件描述符fd发出一个
n=read(fd,fbuffer,BLKSIZE);
系统调用,用数据块填充内部的fbuf[]。然后,它会初始化fbuf[]的指针、计数器和状态变量,以表明内部缓冲区中有一个数据块。
(2) 在随后的每次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 使用I/O库函数或系统调用
对于以BLKSIZE为单位的读/写数据,使用系统调用比I/O库函数更高效。
9.5 I/O库模式
fopen()中的模式参数可以指定为:"r"、"w"、"a",分别代表读、写、追加。 每个模式字符串可包含一个+号,表示同时读写,或者在写入、追加情况下,如果文件不存在则创建文件。
-
"r+":表示读/写,不会截断文件。
-
"w+":表示读/写,但是会先截断文件;如果文件不存在,会创建文件。
-
"a+":表示通过追加进行读/写;如果文件不存在,会创建文件。
9.5.1 字符模式I/O
int fgetc(FILE *fp); //get a char from fp, cast to int
int ungetc(int c, FILE *fp); //push a previously char got by fgetc() back to stream
int fput(int c, FILE *fp); //put a char to fp
注意,fgetc()返回的是整数,而不是字符。这是因为它必须在文件结束时返回文件结束符。文件结束符通常是一个整数-1,将它与文件流中的任何字符区分开。 对于fp=stdin或stdout,可能会使用c=getchar(); putchar(c)来代替。
9.5.2 行模式I/O
行模式I/O以文本文件的行为单位进行存取。
9.5.3 格式化I/O
格式化输入:
从stdin输入:
scanf(char *FMT,&items);
从指定流输入:fscanf(fp,char *FMT, &items);
格式化输出:
输出到stdout:
printf(char *FMT,&items);
输出到指定流:fprintf(fp,char *FMT, &items);
9.5.5 其他I/O库函数
-
fseek()、ftell()、rewind():更改文件流中的读/写字节位置。
-
feof()、ferr()、fileno():测试文件流状态。
-
fdopen():用文件描述符打开文件流。
-
freopen():以新名称重新打开现有的流。
-
setbuf()、setvbuf():设置缓冲方案。
-
popen():创建管道,复刻子进程来调用sh。
9.6 文件流缓冲
每个文件流都有一个FILE结构体,其中包含一个内部缓冲区。对文件流进行读写需要遍历FILE结构体的内部缓冲区。文件流可以使用三种缓冲方案中的一种。
-
无缓冲:从非缓冲流中写入或读取的字符将尽快单独传输到文件或从文件中传输。例如,文件流stderr通常无缓冲。到 stderr 的所有输出都会立即发出。
-
行缓冲:遇到换行符时,写人行缓冲流的字符以块的形式传输。例如,文件流stdout通常是行缓冲,逐行输出数据。
-
全缓冲:写入全缓冲流或从中读取的字符以块大小传输到文件或从文件传输。这是文件流的正常缓冲方案。
9.7 变参函数
在I/O库函数中,printf()相当独特,因为多种不同类型的可变量参数可以调用它。
二、问题与解决思路
1.文件流缓冲
看完书后了解了文件流的三种缓冲方案,但对其实质不甚清楚。
参考https://blog.csdn.net/gltzlike/article/details/119607842
2.fopen、open的区别的是
1.层次不同 open是系统调用,返回的是文件句柄,文件的句柄是文件在文件描述副表里的索引, fopen是ANSIC标准中的C语言库函数,返回的是一个指向文件结构的指针)。在不同的系统中应该调用不同的内核api。linux中的系统函数是open,fopen是其封装函数,fopen的实现要调用open。 2.fopen和open最主要的区别是是否有缓存 fopen用户态下就有了缓存,它使用了FILE这个结构保存缓冲数据。在进行read和write的时候减少了用户态和内核态的切换。 open没有缓存,每次读操作都直接从文件系统中获取数据。在进行read和write的时候每次都需要进行内核态和用户态的切换。 表现为,如果顺序访问文件,fopen系列的函数要比直接调用open系列快;如果随机访问文件open要比fopen快。 3.一般用fopen打开普通文件,用open打开设备文件。 参考:https://blog.csdn.net/qq_26690505/article/details/120201708
三、实践内容与截图
使用linux实现简单的文本操作
linux使用c语言实现文本小写转大写
代码:
#include <stdio.h>
FILE *fp,*gp;
int main(){
char c;
fp=fopen("text.txt","r");
gp=fopen("result.txt","w");
while((c=fgetc(fp))!=EOF){
if(c>='a'&&c<='z')c=c-32;
fputc(c,gp);
}
fclose(fp);
fclose(gp);
return 0;
}
未转换文件text
转换后文件result
可以看到iris Cjc变为大写