Unix/Linux系统编程学习笔记-2

笔记

第九章 I/O库函数

I/O库函数与系统调用

IO库函数是建立在系统调用基础上的:

系统调用 对应的I/O库函数
open() fopen()
read() fread()
write() fwrite()
lseek() fseek()
close() fclose()


二者区别:

  • 1.在系统调用程序中,文件描述符fd是一个整数。在库I/O程序中,fp是一个文件流指针。
  • 2.系统调用open()打开一个文件进行读取,并返回一个整数文件描述符fd,如果open()失败,则返回-1。I/O库函数fopen()返回一个FILE结构体指针,如果fopen()失败,则返回NULL。
  • 3.系统调用程序使用while循环读取/写入文件内容。在每个迭代中,它发出read()系统调用,将最多4KB的字符读入buf[]。然后,它将各字符从buf[]写到文件描述符1中,这是该进程的标准输出。正如前文所指出的,使用系统调用一次写入一个字节非常低效。相反,I/O库程序仅仅使用fgetc(fp)从文件流中获取字符,通过putchar()输出字符,直至文件结束符。
  • 4.fopen() 发出open()系统调用以获取文件描述符fd。如果open()调用失败,将返回一个NULL指针。否则,它会在程序的堆区分配一个FILE结构体。FILE 结构体包含一个内部缓冲区char fbuf[BLKSIZE] 和一个整数fd字段。它记录open()在FILE 结构体中返回的文件描述符,将fbuf[]初始化为空,并将FILE结构体的地址作为fp返回。
  • 5.fgetc(c, fp)尝试从文件流fp中获取一个字符。如果FILE结构体中的fbuf[]为空,则发出read(fd, fbuf, BLKSIZE)系统调用,从文件中读取BLKSIZE字节,其中BLKSIZE与文件系统块大小匹配。然后它从fbuf[]返回一个char。随后,fgetc() 从fbuf[]返回一个char,只要它仍然有数据。这样,库I/O read函数发出read()系统调用,仅用于重新填充fbuf].它们总是将BLKSZISE字节的数据从操作系统内核传输到用户空间。类似表述也适用于I/O write库函数。

I/O库函数的算法

fread算法

在第一次调用fread函数时,FILE中的内部缓冲区fbuf是空的,fread发出read(fd,fbuf,BLKSIZE)系统调用填充缓冲区,并设置fbuf的相关指针,计数器和状态变量,以表明fbuf中的数据情况。然后将数据从fbuf中复制到程序(调用处)的缓冲区中以尝试满足fread函数需求。如果FILE内部缓冲区的数据不够,就再发出一个read系统调用填充fbuf,直到满足fread函数或者到达文件末尾则停止。
即每次fread函数调用都会从FILE结构体的fbuf中复制数据,当缓冲区为空时才发出read系统调用重新填充fbuf。因此fread()一方面函数一方面接收来自用户程序的调用,另一方面根据fbuf情况向操作系统内核发出read系统调用。
fread()函数:从给定输入流stream读取数据到 ptr 所指向的数组中。成功返回所读的元素个数(不是字节数),失败如遇到文件结束或出错时可能返回0。
ptr – 这是指向带有最小尺寸 size*nmemb 字节的内存块的指针。
size – 这是要读取的每个元素的大小,以字节为单位。
nmemb – 这是元素的个数,每个元素的大小为 size 字节。
stream – 文件流。

fwrite算法

初始时FILE结构体内fbuf是空的,fwrite函数将数据写入FILE的fbuf中并调整相关指针、计数器和状态变量。当fbuf满时发出write系统调用将整个缓冲区写入操作系统内核。(这也可以解释当以写方式打开文件时,fclose函数会在关闭文件前将缓冲区内的数据写入到内核缓冲区,因为I/O库函数并不保证当即将数据写入文件)
fwrite()函数:向指定的文件中写入若干数据块,如成功执行则返回实际写入的数据块数目。

fclose算法

关闭文件流。成功返回 0,否则返回 EOF(-1) 并设置全局变量 errno 来表示错误。
如果该文件流以写的方式打开,fclose函数会把内部缓冲区fbuf内最后剩余的数据写入到内核缓冲区(把还没写入的数据写入文件)。fclose会发出close系统调用来关闭FILE结构体中的文件描述符,然后释放FILE结构体并将FILE*重置为NULL

I/O库模式

fopen()中的模式参数可以指定为:"r" 、 "w" 、"a",分别代表读、写、追加。
每个模式字符串可包含一个+号,表示同时读写,或者在写入、追加情况下,如果文件不存在则创建文件。

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

字符模式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 fputc(int c, FILE *fp); //put a char to fp

注意,fget() 返回的是整数,而不是字符。这是因为它必须在文件结束时返回文件结束符。文件结束符通常是一一个整数-1,将它与文件流中的任何字符区分开。
对于fp=stdin或stdout,可能会使用c=getchar(); putchar(c);来代替。对于运行时效来说,getcharO 和putchar()通常不是getc()和pute()的缩小版本。相反,可以将它们实现为宏,以避免额外的函数调用。

行模式I/O

  • char *fgets(char *buf, int size, FILE *fp): 从fp中读取最多一行(以\n结尾)的字符
  • int fputs(char *buf, FILE *fp):将buf中的一行写入fp中。
  • 当fp是stdin或stdout时,也可以使用以下函数,但它们并非fgets()和fputs()的缩减版本。
    gets(char *buf);
    puts(char *buf);

格式化I/O

  • 格式化输入:(FMT=格式字符串)

  • scanf(char *FMT,&item); // from stdin
  • fscanf(fp,char *FMT,&items); // from file stream
  • 格式化输出:

  • printf(char *FMT,item); // to stdout
  • fprintf(fp,char *FMT,items); // to file stream

内存中的转换函数

  • scanf(buf,FMT,&items); // input from buf[] in memory
  • sprintf(buf,FMT,items); // print to buf[] in memory

其他I/O库函数

  • fseek()、ftell()、rewind():更改文件流中的读/写字节位置。
  • feof()、ferr()、fileno(): 测试文件流状态。
  • fdopen(): 用文件描述符打开文件流。
  • freopen(): 以新名称重新打开现有的流。
  • setbuf()、setvbuf(): 设置缓冲方案。
  • popen(): 创建管道,复刻子进程来调用sh。

限制混合fread-fwrite

当某文件流同时用于读/写时,就会限制使用混合fread()和fwrite()调用。规范要求每对fread()和fwrite()之间至少有一个fseek()或ftell()。

文件流缓冲

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

问题与解决思路

问题:

1.使用fread()函数读取数据失败
size_t fread(
void * buffer,
size_t size,
size_t count,
FILE * stream
);

size_t fwrite(
const void * buffer,
size_t size,
size_t count,
FILE * stream
);
2.二进制文件和文本文件如何转换?

  • 解决办法:

  • 1.这两个函数的size的大小是有限制的,fread中,如果size过大,读出数据会失败;fwrite中,size过大,会导致返回的写入字节数不正确,但是数据还是写成功了。size的最大限制是多少目前还没有测试,所以大家使用的时候注意不要把size这个值设置得过大。
  • 2.转载链接https://blog.csdn.net/u012138730/article/details/92986222

实践内容与截图

1.who1.c

代码:

实践:

posted @ 2022-09-11 12:07  20201224吴卓航  阅读(89)  评论(0编辑  收藏  举报