20191302董佳帅第九章学习笔记
第九章 I/O库函数
I/O库函数与系统调用
- 系统调用函数
- open( ):打开和创建文件
- int open(const char * pathname, int flags, mode_t mode);
- read( ):读文件
- ssize_t read[1] (int fd, void *buf, size_t count);
- write( ):写文件
- ssize_t write (int fd, const void * buf, size_t count);
- lseek( )
- 每一个已打开的文件都有一个读写位置, 当打开文件时通常其读写位置是指向文件开头, 若是以附加的方式打开文件(如O_APPEND), 则读写位置会指向文件尾. 当read()或write()时, 读写位置会随之增加,lseek()便是用来控制该文件的读写位置. 参数fildes 为已打开的文件描述词, 参数offset 为根据参数whence来移动读写位置的位移数.
- off_t lseek(int fildes, off_t offset, int whence);
- close( ):关闭文件
- int close(int fd);
- open( ):打开和创建文件
- I/O库函数
- fopen( ):以指定的形式打开文件
r 以只读方式打开文件,该文件必须存在。
r+以读/写方式打开文件,该文件必须存在。
rb+以读/写方式打开一个二进制文件,只允许读/写数据。
rt+以读/写方式打开一个文本文件,允许读和写。
w打开只写文件,若文件存在则长度清为0,即该文件内容消失,若不存在则创建该文件。
w+打开可读/写文件,若文件存在则文件长度清为零,即该文件内容会消失。若文件不存在则建立该文件。
a以附加的方式打开只写文件。若文件不存在,则会建立该文件,如果文件存在,写入的数据会被加到文件尾,即文件原先的内容会被保留(EOF符保留)。
a+以附加方式打开可读/写的文件。若文件不存在,则会建立该文件,如果文件存在,则写入的数据会被加到文件尾后,即文件原先的内容会被保留(原来的EOF符 不保留)。
wb以只写方式打开或新建一个二进制文件,只允许写数据。
wb+以读/写方式打开或建立一个二进制文件,允许读和写。
wt+以读/写方式打开或建立一个文本文件,允许读写。
at+以读/写方式打开一个文本文件,允许读或在文本末追加数据。
ab+以读/写方式打开一个二进制文件,允许读或在文件末追加数据。 - fread( ):从给定流 stream 读取数据到 ptr 所指向的数组中。
- size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream)
ptr -- 这是指向带有最小尺寸 size*nmemb 字节的内存块的指针。
size -- 这是要读取的每个元素的大小,以字节为单位。
nmemb -- 这是元素的个数,每个元素的大小为 size 字节。
stream -- 这是指向 FILE 对象的指针,该 FILE 对象指定了一个输入流。 - fwrite( ):把 ptr 所指向的数组中的数据写入到给定流 stream 中.
- size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream)
- fseek( ):设置流 stream 的文件位置为给定的偏移 offset,参数 offset 意味着从给定的 whence 位置查找的字节数。
- int fseek(FILE *stream, long int offset, int whence)
stream -- 这是指向 FILE 对象的指针,该 FILE 对象标识了流。
offset -- 这是相对 whence 的偏移量,以字节为单位。
whence -- 这是表示开始添加偏移 offset 的位置。它一般指定为下列常量之一。 - fclose( ):关闭流 stream。刷新所有的缓冲区。
- int fclose(FILE *stream)
- fopen( ):以指定的形式打开文件
I/O库函数的算法
- fread算法
- 第一次调用时,fread()使用保存的文件扫描符fd发出 n=read(fd, fbuffer, BLKSIZE);系统调用,用数据块填充内部的fbuff[];
- 初始化fbuff[]指针、计数器和状态变量;
- 将数据复制到程序缓冲区;
- 若内部缓冲没有足够的数据,则使用read()继续填充内部缓冲区,并将数据从内部缓冲区复制到程序缓冲区;
- 复制完之后,更新内部缓冲区的指针、计数器,为下次read()做准备。
- fwrite算法
- 将数据写入内部缓冲区,调整缓冲区指针、计数器和状态变量;
- 若缓冲区满,则调用write()将缓冲区写入系统内核。
- fclose算法
- 关闭文件流局部缓冲区;
- 发出close(fd)系统调用关闭file结构体文件描述符;
- 释放file结构体,并将file指针重置为null。
I/O库模式
- 字符模式I/O
- int fgetc(FILE *fp);
- 该函数以无符号 char 强制转换为 int 的形式返回读取的字符,如果到达文件末尾或发生读错误,则返回 EOF。
- int ungetc(int c, FILE *fp);
- 如果成功,则返回被推入的字符,否则返回 EOF,且流 stream 保持不变。
- int fputc(int c, FILE *fp);
- 如果没有发生错误,则返回被写入的字符。如果发生错误,则返回 EOF,并设置错误标识符。
- int fgetc(FILE *fp);
- 行模式I/O
- char *fgets(char *buf, int size, FILE *fp);
- 从fp中读取最多的为一行(以\n结尾)的字符。
- int fputs(char *buf,FILE *fp);
- 将buf中的一行写入fp中。
- char *fgets(char *buf, int size, FILE *fp);
- 格式化I/O
- 格式化输入
- 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
- fseek()、ftell()、rewind()
文件流缓冲
- 每个文件流都有一个FILE结构体,其中包含一个内部缓冲区。对文件流进行读写需要遍历FILE结构体的内部缓冲区。文件流可以使用三种缓冲方案中的一种。
-
无缓冲:从非缓冲流中写入或读取的字符将尽快单独传输到文件或从文件中传输。
行缓冲:遇到换行符时,写入行缓冲流的字符以块的形式传输。
全缓冲:写入全缓冲流或从中读取的字符以块大小传输到文件或从文件传输。这是文件流的正常缓冲方案。 - setvbuf()
- 定义流 stream 应如何缓冲。
- int setvbuf(FILE *stream, char *buffer, int mode, size_t size);
- fflush()
- 刷新流 stream 的输出缓冲区。
- int fflush(FILE *stream);
最有收获的内容
之前在学习c语言时,我对gets()函数和puts()函数就没有掌握好,他们和scanf()与printf()的区别也没有弄清,通过学习本章和自己进一步上网查找,明白了他们之间的区别:
1、 gets可以接收空格;而scanf遇到空格、回车和Tab键都会认为输入结束,所有它不能接收空格。
例如:如果输入为"hello world"时,上面程序的运行结果是"hello world"。而如果用scanf则只能输出hello
2、scanf对末尾回车符的处理:把回车符保留在缓存中。gets对末尾回车符的处理:接收回车,但把回车替换为\0.
3、gets的返回值为char*型,当读入成功时会返回输入的字符串指针地址,出错时返回NULL;scanf返回值为int型,返回实际成功赋值的变量个数,当遇到文件结尾标识时返回EOF。
4、gets函数仅用于读入字符串;scanf为格式化输出函数,可以读入任意C语言基础类型的变量值,而不是仅限于字符串类型。
问题
- open、write、read和fopen、fwrite、fread的区别到底是什么?
- 经过网上查找,我找到了以下三篇比较不错的回答,很好的解决了问题:
实践练习
编写一个c程序,将文本文件中的字母由小写转换为大写
程序截图:
#include <stdio.h>
FILE *fp,*gp;
int main(){
char c;
fp=fopen("source.txt","r");
gp=fopen("target.txt","w");
while((c=fgetc(fp))!=EOF){
if(c>='a'&&c<='z')c=c-32;
fputc(c,gp);
}
fclose(fp);
fclose(gp);
return 0;
}