## I/O库函数

- 系统调用是文件操作的基础,但是它们只支持数据块的读/写。

**I/O库函数和系统调用**

- 在Unix/Linux中,I/O库函数建立在系统调用的基础上。

- 相互关系

系统调用函数: open ()、 read ()、 write ()、 lseek ()、 close ();

I / O 库函数: fopen ()、 fread ()、 fwrite ()、 fseek ()、 fclose ()。

从中可以看出,I/O库函数的根都在对应的系统调用函数中。

 

- **相同与不同**

相同: I/O库函数和系统调用都

是与输入输出相关的函数。 两者都可以用C语言进行编写。 I/O库函数和系统调用都需要调用底层函数来操作内核资源。

不同:

I/O库函数是标准C库提供的函数,而系统调用是操作系统提供的函数。

I/O库函数是在C语言的标准库中实现的,而系统调用是在内核中实现的。

I/O库函数是通过标准库缓冲区进行输入输出,而系统调用是通过内核缓冲区进行输入输出。

I/O库函数比系统调用更容易使用和理解,但在性能方面没有系统调用高效。

I/O库函数提供了更高级别的接口,而系统调用提供了更底层的接口。

- argc *argv

argc表示命令行参数的个数,其中包含程序名称本身;argv是一个包含了命令行参数字符串的数组指针,其中第一个元素指向程序名称本身,后面的元素指向程序的其它命令行参数。通过使用argc和argv,程序能够接收并处理从命令行传递进来的参数。

## fopen()

- fopen() 是C语言标准库文件操作函数之一,其主要作用是打开一个文件以供读写。它使用字符串表示模式,其中“r”表示读,“w”表示写入。其次它会返回一个指向FILE结构体的指针。

 

- 工作模式

fopen ()首先发出 open ()系统调用来打开文件,以获取文件描述符编号 fd 。如果 open ()系统调用失败,则 fopen ()会返回一个 NULL 指针。否则,它会在程序的堆区中分配一个 FILE 结构体。每个 FILE 结构体均包含一个内部缓冲区 fbuf [ BLKSIZE ],其大小通常与文件系统的 BLKSIZE 相匹配。此外,它还包含用于操作 fbuf []的指针、计数器和状态变量,存储来自 open ()的文件描述符。它将 FILE 结构体初始化并返回指向 FILE 结构体的 fp 。

**需要注意的是, FILE 结构体位于进程的用户模式映像中。这意味着对 I / O 库函数的调用是普通的函数调用,而不是系统调用**

如果任何fopen()调用失败,程序将会终止,并且会返回一个NULL指针。

## I/O库函数的算法

 

- fread算法

(1)第一次调用fread()时,FILE结构体的缓冲区是空的,fread()使用保存的文件描述符fd发出 n = read ( fd , fbuffer , BLKSIZE )

(2) 从文件流中读取指定数量的数据块(block)。

(3) 将读取到的数据块缓存到指定的内存缓冲区(buffer)中,缓冲区的大小为每个数据块的大小 * 数据块数量。

(4) 返回实际读取到的数据块数量,如果读取不足则返回小于指定数量的数据块数量。

(5)如果文件流指向的是文本文件,则读取的数据会被转换为字符型数据,但是在二进制文件中则会直接被以字节(byte)形式写入到缓冲区中。

 

- fwrite算法

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

- fclose算法

I/O库函数中的 fclose 算法的含义是关闭一个已打开的文件流,并将缓冲区中的数据写入文件。

1. 把文件缓冲区的内容写入文件:当打开文件时,系统为了提高文件的访问效率,将文件的内容读取到内存中形成一个输入缓冲区,同时文件的修改操作被存储到一个输出缓冲区中。当执行 fclose 函数时,系统会将输出缓冲区中尚未写入文件的内容强制写入到文件中,并且清空输入缓冲区的内容。

2. 释放文件资源:一个文件在打开时会分配一个文件描述符来标识该文件,同时也会占用一定的内存空间。当执行 fclose 函数时,系统会释放这个描述符,并清除内存中的相关信息,从而释放文件占用的资源。

 

## 使用I/O库函数或系统调用

fread ()依赖 read ()将数据从内核复制到内部缓冲区,然后从内部缓冲区将数据复制到程序的缓冲区。所以,它传输了两次数据。相反, read ()将数据从内核直接复制到程序的缓冲区,只复制了一次。因此,对于以 BLKSIZE 为单位的读/写数据来说, read ()本来就比 fread ()更高效,因为它只需要一个而不是两个复制操作。类似表述也适用于 write ()和 fwrite ().

需要注意,在 fread ()和 fwrite ()的一些实现中,例如在 GNU libc 库中,如果请求的大小以 BLKSIZE 为单位,它们可以使用系统调用将以 BLKSIZE 为单位的数据直接从内核传输到用户指定的缓冲区。即便如此,使用 I / O 库函数仍然需要其他的函数调。因此,在上面的例子中,使用系统调用的程序实际上比使用 I / O 库函数的程序更高效。但是,如果不是以 BLKSIZE 为单位进行读/写,那么 fread ()和 fwrite ()可能更高效。例如,如果我们坚持一次读/写一个字节, fread ()和 fwrite ()会好得多,因为它们进入操作系统内核只是为了填充或清除内部缓冲区,并不是逐字节输入。

 

- I/O库模式

fopen()中的模式参数:“r” , “w” , “a” 分别为读,写,追加。

每个模式字符串可包含一个+号,表示同时读写,或者在写入、追加情况下,如果文件不存在则创建文件。

" r +":表示读/写,不会截断文件。

" w +":表示读/写,但是会先截断文件;如果文件不存在,会创建文件。

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

截断文件:在I/O库模式中,截断文件是指在打开文件时,如果该文件已经存在,文件会被截断至指定的大小,超出部分将被删除。如果文件不存在,则会创建一个新文件。这种操作通常用于清空文件内容,或者重新指定文件大小。在Python中,可以通过使用“w”或“w+”模式来进行文件截断操作。

 

- 行模式I/O

char * fgets ( char * buf , int size , FILE

fp ):从fp中读取最多一行(以\n结尾)的字符字符。

int fputs ( char * buf , FILE * fp ):将 buf 中的一行写入 fp 中。

 

- 其他库函数

1.fseek ()、 ftell ()、 rewind ():更改文件流中的读/写字节位置。

- feof ()、 ferr ()、 fileno ():测试文件流状态。

- fdopen ():用文件描述符打开文件流。

- freopen ():以新名称重新打开现有的流。

5.setbuf ()、 setvbuf ():设置缓冲方案。

6.popen ():创建管道,复刻子进程来调用 sh 。

 

-文件流缓冲

每个文件流都有一个 FILE 结构体,其中包含一个内部缓冲区。对文件流进行读写需要遍历 FILE 结构体的内部缓冲区。

## 文件流缓冲的方案:

1. 无缓冲:从非缓冲流中写入或读取的字符将尽快单独传输到文件或从文件中传输。

2. 行缓冲:遇到换行符时,写人行缓冲流的字符以块的形式传输。

3. 全缓冲:写入全缓冲流或从中读取的字符以块大小传输到文件或从文件传输。这是文件流的正常缓冲方案。

 

-编程项目:类printf函数

编程项目会编写一个类 printf ()函数,用于格式化打印字符、字符串、无符号整数、十进制有符号整数和十六进制无符号整数。

 

## -项目规范

本编程项目会编写一个类 printf ()函数,用于格式化打印字符、字符串、无符号整数、十进制有符号整数和十六进制无符号整数。编程项目的目的是让读者了解 I / O 库函数是如何实现的。

在 Linux 中, putchar ( char c )可打印一个字符。

用于格式化打印其他参数,其中 fmt 是格式字符串,包含:

% c : print char 打印字符

% s : print string 打印字符串

% u : print unsigned integer 打印无符号整数

% d : print signed integer 打印带符号整数

% X : print unsigned integer in HEX 以十六进制打印无符号整数

 

-myprintf()的算法

假设格式字符串 fmt =" char =% c string =% s integer =% d

u32=% x \ n "。这意味着分别有 char 、 char *、 int 、 unsigned int 和 type 的4个附加参数。 myprint ()的算法如下:

(1)扫描格式字符串 fmt 。打印任何不是%的字符。对于每个' \n '字符,打印一个额外的'\ r '字符。

(2)当遇到'%'时,得到的下一个字符必须是' c '、' s '、' u '、' d '或' x '中的一个。使 va _ arg ( ap , type )来提取相应的参数。然后通过参数类型调用打印函数。

(3)当 fmt字符串扫描结束时,算法结束。

 

苏格拉底挑战

1

2

 

运用AIchat询问问题

 

posted on 2023-09-16 23:18  灰灰爱跳舞  阅读(9)  评论(0编辑  收藏  举报