20191323王予涵第九章学习笔记
20191323王予涵第九章学习笔记
一、知识点总结与收获
I/O库函数与程序调用
系统调用函数:
-
open():打开和创建文件。int
-
read():读文件。*ssize_t read (int fd, void buf, size_t count);
fd记录的是读取文件位置由open函数返回得到。
返回值:成功返回读取的字节数,出错返回-1并设置errno,如果在调read之前已到达文件末尾,则这次read返回0。
-
write():把参数buf所指的内存写入count个字节到参数fd所指的文件内。ssize_t write (int fd, const void * buf, size_t count);
-
lseek():用于改变读写一个文件时读写指针位置。off_t lseek(int fildes, off_t offset, int whence);
参数 whence :
SEEK_SET 参数offset 即为新的读写位置;
SEEK_CUR 以目前的读写位置往后增加offset 个位移量;
EEK_END 将读写位置指向文件尾后再增加offset 个位移量;返回值:当调用成功时则返回目前的读写位置
-
close():关闭一个已经打开的文件。int close(int fd);
I/O库函数:
-
fopen():打开一个文件。 FILE * fopen(const char * path, const char * mode);
返回值:返回指向file结构体的指针。
mode参数:
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 对象指定了一个输入流。
返回值:成功读取的元素总数会以 size_t 对象返回,size_t 对象是一个整型数据类型。
-
fwrite():把 ptr 所指向的数组中的数据写入到给定流 stream 中。**size_t fwrite(const void ptr, size_t size, size_t nmemb, FILE stream) ;
参数:
ptr -- 这是指向要被写入的元素数组的指针。
size -- 这是要被写入的每个元素的大小,以字节为单位。
nmemb -- 这是元素的个数,每个元素的大小为 size 字节。
stream -- 这是指向 FILE 对象的指针,该 FILE 对象指定了一个输出流。
返回值:成功写入的元素总数会以 size_t 对象返回。**size_t fwrite(const void ptr, size_t size, size_t nmemb, FILE stream);
-
fseek():设置流 stream 的文件位置为给定的偏移 offset。 int fseek(FILE *stream, long int offset, int whence);
whence参数:
SEEK_SET 文件的开头 SEEK_CUR 文件指针的当前位置 SEEK_END 文件的末尾 返回值:如果成功,则该函数返回零,否则返回非零值。
-
fclose():关闭流 stream,刷新所有的缓冲区。*int fclose(FILE stream);
返回值:如果流成功关闭,则该方法返回零。如果失败,则返回 EOF。
I/O库函数的算法
fread()算法:
- fread()使用之前得到的fd使用read()发出系统调用,填充file结构体中的fbuff[];
- 初始化fbuff[]指针、计数器和状态变量;
- 将数据复制到程序缓冲区;
- 若内部缓冲没有足够的数据,则使用read()继续填充内部缓冲区,并将数据从内部缓冲区复制到程序缓冲区;
- 复制完之后,更新内部缓冲区的指针、计数器,为下次read()做准备;
fwrite()算法:
- 将数据写入内部缓冲区,调整缓冲区指针、计数器和状态变量;
- 若缓冲区满,则调用write()将缓冲区写入系统内核;
fclose()算法:
- 关闭文件流局部缓冲区;
- 发出close(fd)系统调用关闭file结构体文件描述符;
- 释放file结构体,并将file指针重置为null;
使用I/O库函数或系统调用
请求大小以BLKSIZE(Linux为4096)为单位,它们可以使用系统调用将以BLKSIZE为单位的数据直接从内核输出到用户指定的缓冲区。若不以BLKSIZE为单位进行R/W,则fread()和fwrite()更高效。
I/O库模式
字符模式I/O
-
fgetc():从指定的流 stream 获取下一个字符(一个无符号字符),并把位置标识符往前移动。int fgetc(FILE *stream);
返回值:该函数以无符号 char 强制转换为 int 的形式返回读取的字符,如果到达文件末尾或发生读错误,则返回 EOF。
-
ungetc():把字符 char(一个无符号字符)推入到指定的流 stream 中,以便它是下一个被读取到的字符。int ungetc(int char, FILE *stream);
参数:
char -- 这是要被推入的字符。该字符以其对应的 int 值进行传递。
stream -- 这是指向 FILE 对象的指针,该 FILE 对象标识了输入流。
返回值:如果成功,则返回被推入的字符,否则返回 EOF,且流 stream 保持不变。
-
fputc()或putc():把参数 char 指定的字符(一个无符号字符)写入到指定的流 stream 中,并把位置标识符往前移动。int fputc(int char, FILE *stream);
参数:
char -- 这是要被写入的字符。该字符以其对应的 int 值进行传递。
返回值:如果没有发生错误,则返回被写入的字符。如果发生错误,则返回 EOF,并设置错误标识符。
行模式I/O
-
fgets()或gets():从指定的流 stream 读取一行,并把它存储在 str 所指向的字符串内。当读取 (n-1) 个字符时,或者读取到换行符时,或者到达文件末尾时,它会停止。char *fgets(char *str, int n, FILE *stream);
参数:
str -- 这是指向一个字符数组的指针,该数组存储了要读取的字符串。
n -- 这是要读取的最大字符数(包括最后的空字符)。通常是使用以 str 传递的数组长度。
stream -- 这是指向 FILE 对象的指针,该 FILE 对象标识了要从中读取字符的流。
返回值:如果成功,该函数返回相同的 str 参数。如果到达文件末尾或者没有读取到任何字符,str 的内容保持不变,并返回一个空指针。如果发生错误,返回一个空指针。
-
fputs()或puts():把字符串写入到指定的流 stream 中,但不包括空字符。int fputs(const char *str, FILE *stream);
参数:
str -- 这是一个数组,包含了要写入的以空字符终止的字符序列。
stream -- 这是指向 FILE 对象的指针,该 FILE 对象标识了要被写入字符串的流。
返回值:该函数返回一个非负值,如果发生错误则返回 EOF。
格式化I/O
格式化输入:
-
scanf(): int scanf(const char *format, ...) 从标准输入 stdin 读取格式化输入。
-
fscanf(): int fscanf(FILE *stream, const char *format, ...) 从流 stream 读取格式化输入。
返回值:如果成功,该函数返回成功匹配和赋值的个数。如果到达文件末尾或发生读错误,则返回 EOF。
格式化输出:
-
printf(): int printf(const char *format, ...) 发送格式化输出到标准输出 stdout。
返回值:如果成功,则返回写入的字符总数,否则返回一个负数。
-
fprintf(): int fprintf(FILE *stream, const char *format, ...) 发送格式化输出到流 stream 中。
返回值:如果成功,则返回写入的字符总数,否则返回一个负数。
内存中的转换函数:
-
sscanf(): int sscanf(const char *str, const char *format, ...) 从字符串读取格式化输入。
-
sprintf():int sprintf(char *str, const char *format, ...) 发送格式化输出到 str 所指向的字符串。
参数:
str -- 这是指向一个字符数组的指针,该数组存储了 C 字符串。
format -- 这是字符串,包含了要被写入到字符串 str 的文本。它可以包含嵌入的 format 标签,format 标签可被随后的附加参数中指定的值替换,并按需求进行格式化。
返回值:如果成功,则返回写入的字符总数,不包括字符串追加在字符串末尾的空字符。如果失败,则返回一个负数。
其他I/O库函数:
- fseek()、ftell()、rewind():更改文件流中的读/写字节位置。
- feof()、ferr()、fileno():测试文件流状态。
- fdopen():用文件描述符打开文件流。
- freopen():以新名称重新打开现有的流。
- setbuf()、setvbuf():设置缓冲方案。
- popen():创建管道,复刻子进程来调用sh。
文件流缓冲
每个文件流都有一个FILE结构体,其中包含一个内部缓冲区。对文件流进行读写需要遍历FILE结构体的内部缓冲区。文件流可以使用三种缓冲方案中的一种。
- 无缓冲:从非缓冲流中写入或读取的字符将尽快单独传输到文件或从文件中传输。
- 行缓冲:遇到换行符时,写入行缓冲流的字符以块的形式传输。
- 全缓冲:写入全缓冲流或从中读取的字符以块大小传输到文件或从文件传输。这是文件流的正常缓冲方案。
-
setvbuf():定义流 stream 应如何缓冲。int setvbuf(FILE *stream, char *buffer, int mode, size_t size);
参数:
stream -- 这是指向 FILE 对象的指针,该 FILE 对象标识了一个打开的流。
buffer -- 这是分配给用户的缓冲。如果设置为 NULL,该函数会自动分配一个指定大小的缓冲。
mode -- 这指定了文件缓冲的模式。
缓冲模式:
_IOFBF 全缓冲:对于输出,数据在缓冲填满时被一次性写入。对于输入,缓冲会在请求输入且缓冲为空时被填充。
_IOLBF 行缓冲:对于输出,数据在遇到换行符或者在缓冲填满时被写入,具体视情况而定。对于输入,缓冲会在请求输入且缓冲为空时被填充,直到遇到下一个换行符。
_IONBF 无缓冲:不使用缓冲。每个 I/O 操作都被即时写入。返回值:如果成功,则该函数返回 0,否则返回非零值。
-
fflush():刷新流 stream 的输出缓冲区。 int fflush(FILE *stream);
stream -- 这是指向 FILE 对象的指针,该 FILE 对象指定了一个缓冲流。
返回值:如果成功,该函数返回零值。如果发生错误,则返回 EOF,且设置错误标识符(即 feof)。
问题
文件操作都有什么?
- 打开文件
- 读取和追加数据
- 插入和删除数据
- 关闭文件
- 删除文件
........
二进制和文本文件如何转换?
- 对二进制文件中的二进制数据以16位为一组转换为相应的ascii码
- 在文本文件中直接以二进制格式输出即可
数据结构如何读写?
在c语言中file被定义成为一个结构体,该结构体的具体定义如下
typedef struct _iobuf {
int cnt; // 剩余的字符,如果是输入缓冲区,那么就表示缓冲区中还有多少个字符未被读取
char *ptr; // 下一个要被读取的字符的地址
char *base; // 缓冲区基地址
int flag; // 读写状态标志位
int fd; // 文件描述符
// 其他成员
} FILE;
open()返回的文件指针即为该结构体的指针,如果直接采用->常规方式对此结构体进行访问是非常危险的。此时对此结构体进行读写操作要使用到文件操作函数来进行读写。
实践
将文本文件由小写转换为大写
makefile代码:
upper: upper.c
gcc -g -o $@ $<
C代码:
#include <stdio.h>
#include <ctype.h>
int main(void){
FILE *fp;
int c;
fp = fopen("/home/wyh/lessontest/upper/text/textfile","rt+");
while((c = fgetc(fp)) != EOF){
if(islower(c)){
fseek(fp, -1L, SEEK_CUR);
fputc(toupper(c), fp);
}
}
fclose(fp);
return 0;
}
过程截图:
未执行程序之前文本文件:
执行程序之后文本文件