学习笔记第九章

第九章

一、I/O库函数与系统调用

(一)open()和fopen()相同点和不同点

学习笔记第九章

第1行:在系统调用程序中,文件描述符fd是一个整数;在库I/O程序中,fp是一个文件流指针。
第2行:系统调用open()打开一个文件进行读取,并返回一个整数文件描述符fd,弱国open()失败,则返回-1.I/O库函数fopen()返回一个FILE结构体指针,如果fopen()失败,则返回NULL。
第3行:系统调用程序使用while循环读取/写入文件内容。在每个迭代中,它发出read()系统调用,将最多4KB的字符读入buf[]。然后,它将个字符从buf[]写道文件描述符l中,这是该进程的标准输出。使用系统调用一次写入一个字节非常低效。相反,I/O库程序仅仅使用fgetc(fp)从文件流中获取字符,通过putchar()输出字符,直至文件结束。

学习笔记第九章

这两个程序都会将src文件复制到dest文件中。由于第6章已经解释过系统调用程序,所以我们只讨论使用1/O库函数的程序。

(1) fopen()使用字符串表示模式,其中“r”表示READ,“w”表示 WRITE。它返回一个指向FILE结构体的指针。fopen()首先发出open()系统调用来打开文件,以获取文件描述符编号f。如果 open()系统调用失败,则 fopen()会返回一个NULL指针。否则,它会在程序的堆区中分配一个FILE结构体。每个FILE结构体均包含一个内部缓冲区 fbuf[BLKSIZE],其大小通常与文件系统的BLKSIZE相匹配。此外,它还包含用于操作 fbuf]的指针、计数器和状态变量,存储来自open()的文件描述符。它将FILE结构体初始化并返回指向FILE结构体的f。需要注意的是,FILE结构体位于进程的用户模式映像中。这意味着对I/O库函数的调用是普通的函数调用,而不是系统调用。
(2)如有任何fopen()调用失败,程序将会终止。如前文所述,fopen()在失败时会返回一个NULL指针,例如,文件不能在指定模式下打开时。
(3)然后,它使用一个while循环来复制文件内容。while循环的每个迭代尝试从源文件读取BLKSIZE 字节,并向目标文件写人n个字节,其中n是从fread()返回的值。fread()'和 fwrite()的一般形式是:

int n=fread(buffer,size,nitems,FILEpet)
int n=fwrite(buffer,size,nitems,FILEpet)

系统函数:

  • open(),用于创建一个新的文件描述符;
  • read(),读取文件,从文件描述符 fildes 相关联的文件里读入 nbytes 个字节的数据,并把它们放到数据区 buffer 中;
  • write(),把缓冲区 buffer 的前 nbytes 个字节写入与文件描述符 fildes 关联的文件中;
  • lseek(),用于改变读写操作时的位置指针;
  • close(),终止文件描述符 fildes 与其对应文件之间的关联。

I/O库函数:

  • fopen(),主要用于对文件和终端的输入输出;
  • fread(),主要作用是从一个文件流里读取数据,数据从stream读到由ptr指定的数据缓冲区里面; fwirte(),从stream获取数据记录写到ptr中,返回值是成功写入的记录个数;
  • fseek(),在文件流里面为下一次读写指定位置;
  • fclose(),关闭指定的文流stream,使所有未写出的内容全部写出。
    可以看出来,二者之间的函数功能基本可以一一对应,但是在系统调用中,使用了整数型变量文件描述符fd,而在I/O库函数中则使用了文件流指针fp;系统调用的buffer最多包含4KB的字符,并且一般使用while循环一次一个字节的写入数据,效率十分低下。而I/O库函数可以使用fgetc(fp)将文件流中的所有数据读取,十分高效。

二、I/O库函数的算法

(一)fread()

(1)在第一次调用fread()时,FILE结构体的缓冲区是空的,fread()使用保存的文件描述符fd发出一个
n=read(fa, fbuffer,BLKSIZE);
系统调用,用数据块填充内部的fbuf[]。然后,它会初始化 fbuf[]的指针、计数器和状态变量,以表明内部缓冲区中有一个数据块。接着,通过将数据复制到程序的缓冲区,尝试满足来自内部缓冲区的fread()调用。如果内部缓冲区没有足够的数据,则会再发出一个read()系统调用来填充内部缓冲区,将数据从内部缓冲区传输到程序缓冲区,直到满足所需的字节数(或者文件无更多数据)。将数据复制到程序的缓冲区之后,它会更新内部缓冲区的指针、计数器等,为下一个fread()请求做好准备。然后,它会返回实际读取的数据对象数量。
(2)在随后的每次fread()调用中,它都尝试满足来自FILE结构体内部缓冲区的调用。当缓冲区变为空时,它就会发出read()系统调用来重新填充内部缓冲区。因此,fread()一方面接受来自用户程序的调用,另一方面向操作系统内核发出read()系统调用。

(二)fwrit()

fwrite()算法与fread()算法相似,只是数据传输方向不同。最开始,FILE结构体的内部缓冲区是空的。在每次调用fwrite()时,它将数据写入内部缓冲区,并调整缓冲区的指针、计数器和状态变量,以跟踪缓冲区中的字节数。如果缓冲区已满,则发出write()系统调用,将整个缓冲区写入操作系统内核。

(三)fclose算法

若文件以写的方式被打开,fclose()会先关闭文件流的局部缓冲区。然后,它会发出一个close(fd)系统调用来关闭FILE结构体中的文件描述符。最后,它会释放FILE结构体,并将FILE指针重置为NULL。

三、I/O库函数模式

"r+":表示读/写,不会截断文件。
"w+":表示读/写,但是会先截断文件;如果文件不存在,会创建文件。
"a+":表示通过追加进行读/写;如果文件不存在,会创建文件。

四、限制混合fread-fwrite

fread()fwrite()会发出read()/write()系统调用来填充/清除内部缓冲区。当read()/write()使用文件OFTE 中的读/写指针时,fread()/fwrite()会使用FILE结构体中局部缓冲区的读/写指针。
如果没有fseek()来同步这两个指针,其结果就取决于它们在实现中的使用方式。为了避免出现任何不一致,我们将下面一行:
fseek (fp,(long) 20,0 ) ;
插入fread()和 fwrite()中间,结果会相同(而且正确)。

五、文件流缓冲

无缓冲:从非缓冲流中写入或读取的字符将尽快单独传输到文件或从文件中传输。例
如,文件流stderr通常无缓冲。到stderr的所有输出都会立即发出。
行缓冲:遇到换行符时,写人行缓冲流的字符以块的形式传输。例如,文件流stdout
通常是行缓冲,逐行输出数据。
全缓冲:写人全缓冲流或从中读取的字符以块大小传输到文件或从文件传输。这是文
件流的正常缓冲方案。

最有收获的内容

本章中最有收获的内容是I/O库函数的算法,从底层了解了三个函数的详细调用方法。

问题与解决思路

问题一:课本中多次提到了“宏”,不是很理解他的具体意义。
解决方法:通过百度后了解到:预处理命令可以改变程序设计环境,提高编程效率,它们并不是 C 语言本身的组成部分,不能直接对 它们进行编译,必须在对程序进行编译之前,先对程序中这些特殊的命令进行“预处理” 。经过预处理后,程序就不再包括预处理命令了,最后再由编译程序对预处理之后的源程序进行编译处理,得到可供执行的目标代码。C 语言提供的预处理功能有三种,分别为宏定义、文件包含和条件编译。
链接:https://blog.csdn.net/imgosty/article/details/81901183

上一篇:php文件操作


下一篇:fread函数常见错误