第九章 I/O库函数
一、知识归纳与总结
本章讨论了I/O库函数解释了I/O库函数的作用及其相对于系统调用的优势;使用示例程序来说明I/O库函数和系统调用之间的关系,并解释了它们之间的相似性和基本区别;详细介绍了I/O库函数的算法,包括fread、fwrite和 fclose 的算法,重点介绍了它们与 read、write 和 close 系统调用的交互;介绍了I/O库函数的不同模式,包括字符模式、行模、结构化记录模式和格式化I/O操作;阐述了文件流缓冲方案,并通过示例程序说明了不同缓冲方案的效果;阐释了有不同参数的函数以及如何使用stdarg 宏访问参数。
- I/O库函数:系统调用是文件操作的基础,但它们只支持数据块的读/写。
- I/O库函数与系统调用:在UNIX/Linux中,I/O库函数建立在系统调用的基础上。(为什么系统调用的效率比库函数要低很多?因为使用系统调用会影响系统的性能。与函数调用相比,系统调用时,Linux必须从运行用户代码切换到执行内核代码,然后再返回用户代码,所以系统调用的开销要比普通函数调用大一些。然而也是有办法减少这种开销的,就是在程序中尽量减少系统调用的的次数,并且让每次系统调用完成尽量多的工作。而库函数为什么做同样的事情效率却会高这么多呢?这是因为库函数在数据满足数据块长度(或buffer长度)要求时才安排执行底层系统调用,从而减少了系统调用的次数,也让每次的系统调用做了尽量多的事情,所以效率就比较高。https://blog.csdn.net/ljianhui/article/details/10055665)
- 系统调用函数:open() read() write() lseek() close()
- I/O库函数:fopen() fread() fwrite() fseek() fclose()
I/O库函数算法
- fread算法:在第一次调用freadO时,FILE结构体的缓冲区是空的,freadO使用保存的文件描述符fd发出一个n = read(fd, fbuffer, BLKSIZE);系统调用,用数据块填充内部的fbuf[]。然后,它会初始化fbuf[]的指针、计数器和状态变量,以表明内部缓冲区中有一个数据块。接着,通过将数据复制到程序的缓冲区,尝试满足来自内部缓冲区的fread调用。如果内部缓冲区没有足够的数据,则会再发出一个 read()系统调用来填充内部缓冲区,将数据从内部缓冲区传输到程序缓冲区,直到满足所需的字节数(或者文件无更多数据)。将数据复制到程序的缓冲区之后,它会更新内部缓冲区的指针、计数器等,为下一个fread()请求做好准备。然后,它会返回实际读取的数据对象数量。
在随后的每次 fread() 调用中,它都尝试满足来自FILE 结构体内部缓冲区的调用。当缓冲区变为空时,它就会发出read()系统调用来重新填充内部缓冲区。因此,fread()一方面接受来自用户程序的调用,另一方面向操作系统内核发出read()系统调用。除了read()系统调用之外,所有fread()处理都在用户模式映像中执行。它只在需要时才会进入操作系统内核,并且以一种最高效匹配文件的方式进入。它会提供自动缓冲机制,因此用户程序不必担心这些具体操作。 - fwrite算法:fwrite()算法与fread()算法相似,只是数据传输方向不同。最开始,FILE结构体的内部缓冲区是空的。在每次调用fwrite()时,它将数据写入内部缓冲区,并调整缓冲区的指针、计数器和状态变量,以跟踪缓冲区中的字节数。如果缓冲区已满,则发出 write()系统调用,将整个缓冲区写入操作系统内核。
- fclose算法:若文件以写的方式被打开,fcloseO会先关闭文件流的局部缓冲区。然后,它会发出一个close(fd)系统调用来关闭FILE结构体中的文件描述符。最后,它会释放FILE结构体,并将 FILE 指针重置为 NULL。
I/O库模式包含字符模式I/O,行模式I/O,格式化I/O和其他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 *fpl; // push a previously char got by fgetc() back to stream
int fputc(int c, FILE *fp); // put a char to fp
- fgetc()返回的是整数,而不是字符。这是因为它必须在文件结束时返回文件结束符。文件结束符通常是一个整数-1,将它与文件流中的任何字符区分开。
- 对于fp=stdin或stdout,可能会使用c=getchar();putchar(c);来代替。对于运行时效说,getchar()和putchar()通常不是getc()和putc()的缩小版本。相反,可以将它们实现为宏,以避免额外的函数调用。
- 行模式I/O:原型
点击查看代码
char *fgets(char *buf, int size,FILE*fp) //从fp中读取最多为一行(以
结尾)的字符。
int fputs(char *buf,FILE*fp) //将buf中的一行写人fp中。
- 当 fp 是 stdin 或 stdout 时,也可以使用gets(char *buf); // input line from stdin but without checking length或puts(char *buf);// write line to stdout
- 格式化I/O:
输入:(FMT=格式字符串)
scanf(char*FMT, &items); // from stdin
fscanf(fp,char *FMT, &items); // from file stream
输出:
printf(char *FMT, items); // to stdout
fprintf(fp,char *FMT, items); // to file stream - 内存中的转换函数:
sscanf(buf,FMT,&items); // input from buf[ ] in memory
sprintf(buf,FMT,items); // print to buf[ ] in memroy - 其他I/O库函数:
fseek()、ftell()、rewind():更改文件流中的读/写字节位置
feof()、ferr()、fileno():测试文件流状态
fdopen():用文件描述符打开文件流
freopen():以新名称重新打开现有的流
setbuf()、setvbuf():设置缓冲方案
popen():创建管道,复刻子进程来调用sh
文件流缓冲
- 无缓冲:从非缓冲流中写入或读取的字符将尽快单独传输到文件或从文件中传输
- 行缓冲:遇到换行符时,写入行缓冲流的字符以块的模式传输
- 全缓冲:写入全缓冲流或从中读取的字符以块大小传输到文件或从文件传输
- 通过fopen()创建文件流之后,在对其执行任何操作之前,用户均可发出一个setvbuf(FILE *stream, char *buf, int node, int size)调用来设置缓冲区(buf)、缓冲区大小(size)和缓冲方案(mode),它们必须是以下一个宏:
IONBUF:无缓冲
IOLBUF:行缓冲
IOFBUF:全缓冲
此外,还有其他的setbuf()函数,是setvbuf()的变体。对于行缓冲流或全缓冲流,可用fflush(stream)立即清除流的缓冲区。 -
文件操作都有什么?
1.建立文件名、扩展名、文件属性确定
2.修改(包括文件名、扩展名、内容、文件属性的修改,内容修改包括改写、插入、添加)
3.存储位置的改变(文件移动):包括路径、磁盘变更
4.复制
5.删除
-
二进制文件和文本文件如何转换?
使用vim修改:vim -b 文件名
:%!xxd ——转化成16进制
然后进行修改二进制
:%!xxd -r——转回文本文件
:wq
-
数据结构如何读写?
代码实现:
点击查看代码
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
//第一步:打开文件
int fd=-1; //fd是文件描述符(在linux中的文件描述符fd 的合法范围是0或者是一个正数,不可能是负数)
fd=open("a.txt",O_RDWR);//O_RDWR表示文件可读可写,这个可以 用man 手册查看open函数的使用方法里面有它的说明
if(-1 ==fd)或者(fd<0)
{
printf("文件打开错误
");
}
else
{
printf("文件打开成功
");
}
//读写文件
//关闭文件
close(fd);//关闭刚才打开的文件
return 0;
}
实践(都用的I/O库函数)
示例9.1:
示例9.2:
将test2.txt复制到testio1.txt:
结果:
示例9.3:
示例9.4:
src中是两行文本,dest中输出一行: