结构化教材内容
第9章:I/O库函数
I/O库函数与系统调用
- 系统调用是文件操作的基础,但它们只支持数据块的读/写。
- 系统调用函数:open()、read()、write()、lseek()、close();
- I/O库函数:fopen()、fread()、fwrite()、fseek()、fclose();
- I/O库函数的根都在对应的系统调用函数中,例fopen()依赖于open()。
I/O库函数的算法
- fread算法
- 第一次调用fread()时,FILE结构体缓冲区是空的,fread()使用保存的文件描述符fd发出一个
n = read(fd, fbuffer, BLKSIZE);
系统调用,用数据块填充内部的fbuf[]。然后它会初始化fbuf[]的指针、计数器和状态变量,表明内部缓冲区有一个数据块。接着,通过将数据赋值到程序的缓冲区,尝试满足来自内部缓冲区的fread()调用。如果内部缓冲区没有足够的数据,则会再发出一个read()系统调用来填充内部缓冲区,将数据从内部缓冲区传输到程序缓冲区,直到满足所需的字节数(或者文件无更多数据)。将数据复制到程序的缓冲区之后,它会更新内部缓冲区的指针、计数器等,为下一个fread()请求做好准备。然后它会返回实际读取的数据对象数量。 - 在随后的每次fread()调用中,它都尝试满足来自FILE结构体内部缓冲区的调用。当缓冲区变为空时,它就会发出read()系统调用来重新填充内部缓冲区。因此fread()一方面接受来自用户程序调用,另一方面向操作系统内核发出read()系统调用。除了read()系统调用外,所有fread()处理都在用户模式映像中执行。它只在需要时才会以一种最搞笑匹配文件的方式进入操作系统内核,并且会提供自动缓冲机制。
-
fwrite算法
与fread()算法类似,只是数据传输方向不同。最开始FILE结构体内部缓冲区为空,每次调用fwrite()时,将数据写入内部缓冲区,并调整缓冲区的指针、计数器和状态变量,以跟踪缓冲区中的字节数。如果缓冲区已满,则发出write()系统调用,将整个缓冲区写入操作系统内核。 -
fclose算法:
若文件以写方式打开,fclose()会先关闭文件流的局部缓冲区。然后发出close(fd)系统调用来关闭FILE结构体中的文件描述符,最后释放FILE结构体并将FILE指针重置为NULL。
I/O库模式
fopen()模式参数可指定为:"r"、"w"、"a",分别代表读、写、追加,后面添加"+",表示同时读写,或者在写入、追加情况下,若文件不存在则创建文件。
"r+":表示读/写,不会截断文件。
"w+":表示读/写,但是会先截断文件;如果文件不存在,会创建文件。
"a+":表示通过追加进行读/写;如果文件不存在,会创建文件。
- 字符模式I/O:
int fgetc(FILE *fp);
int ungetc(int c, FILE *fp);
int fputc(int c, FILE*fp);
当fp=stdin或stdout,可能会使用c=getchar();putchar(c);来代替。
- 行模式I/O:
char *fgets(char *buf, int size, FILE *fp);
int fputs(char *buf, FILE *fp);
当fp=stdin或stdout时也可以使用
gets(char *buf);
puts(char *buf);
- 格式化I/O:
格式化输入:(FMT=格式字符串)
scanf(char *FMT, &items);
fscanf(fp, char *FMT, &items);
格式化输出:
printf(char *FMT, items);
fprintf(fp, char *FMT, items);
- 内存中的转换函数
sscanf(buf, FMT, &items);
sprintf(buf, FMT, items);
-
其他I/O库函数
fseek()、ftell()、rewind():更改文件流中的读/写字节位置。
feof()、ferr()、fileno():测试文件流状态。
fdopen():用文件描述符打开文件流。
freopen():以新名称重新打开现有的流。
setbuf()、setvbuf():设置缓冲方案。
popen():创建管道,复刻子进程来调用sh。 -
限制混合fread-fwrite
当某文件流同时用于读/写时,就会限制使用混合fread()和fwrite()调用。规范要求每对fread()和fwrite()之间至少有一个fseek()或ftell()。
文件流缓冲
对文件流进行读写需要遍历FILE结构体的内部缓冲区。文件流可使用三种缓冲方案中的一种:
- 无缓冲:从非缓冲流中写入或者读取的字符将尽快单独传输到文件或从文件中传输。
- 行缓冲:遇到换行符时,写入到缓冲流的字符以块的形式传输。
- 全缓冲:写入全缓冲或从中读取的字符以块大小传输文件或从文件传输。这是文件流的正常缓冲方案。
通过fopen()创建文件流之后,在执行任何操作前均可发出
setvbuf(FILE *stream, char *buf, int mode, int size)
调用来设置缓冲区(buf),缓冲区大小(size)和缓冲方案(mode),它们必须是以下一个宏:
_IONBF:无缓冲
_IOLBF:行缓冲
_IOFBF:全缓冲
变参函数
在I/O库函数中,多种不同类型的可变数量参数可以调用printf()。目前C和C++允许参数数量可变的函数,这些函数必须至少使用一个参数进行声明,后跟3个点,如:int func(int m, int n . . . )
在函数内部可以通过C语言库宏访问参数:
void va_start(va_list ap, last);
type va_arg(va_list ap, type);
va_end(va_list ap);
问题总结
文件操作都有什么?
文件操作包括打开文件、关闭文件、读取文件、写入文件、定位文件指针、删除文件和创建文件等。
二进制文件和文本文件如何转换?
二进制文件转文本文件:
使用xxd命令可以将二进制文件转换为文本文件。xxd命令会将二进制文件的内容以十六进制形式显示在终端上,并将其保存到一个文本文件中。
xxd -p binary_file > text_file
上述命令中,binary_file是要转换的二进制文件的文件名,text_file是要生成的文本文件的文件名。
文本文件转二进制文件:
使用xxd命令的-r选项可以将文本文件转换为二进制文件。
xxd -r -p text_file > binary_file
上述命令中,text_file是要转换的文本文件的文件名,binary_file是要生成的二进制文件的文件名。
数据结构如何读写
使用标准I/O库函数:可以使用标准I/O库函数(如fopen、fread、fwrite、fclose等)来打开文件、读取数据、写入数据和关闭文件。这些函数可以通过文件指针来访问文件,并使用缓冲区来读写数据。
使用系统调用:可以使用系统调用(如open、read、write、close等)来打开文件、读取数据、写入数据和关闭文件。
GPT互动提问
-
I/O库函数
-
文件流缓冲