1.标准库的IO接口
标准库io接口:fopen / fwrite/ fread/ fseek/ fclose/ printf/ fprintf/ gets/ fgets/ scanf
FILE *fopen(const char *path, const char *mode); — 打开一个文件
path:要打开文件的名称/
mode:打开文件方式“r”–只读 /“r+”–读写 /“w”–只写 /‘w+’–读写 /“a+”–读追加写 /“b”–二进制
r+"读写/“w+”-读写: w打开文件时, 若文件不存在则创建文件,存在则清空文件原有内容;而r若文件不存在, 则打开时会报错
'r+"读写/'a+“读追加写: a追加写,每次向文件写入数据都会写入到文件末尾,文件不存在也会创建文件
“b”-二进制:默认情况下文件数据操作是文本操作,b指的是将文件数据当作二进制数据进行操作; -有的特殊字符,字符只是一个, 但是在存储空间内的二进制数据占据好几个字节。(假如想要读取100个字 节的数据,实际读取到可能只有90个字符,因为其中有某个字符可能占了好几个字节
把文件数据全部读取出来,,首先获取文件大小, 然后一次性读取,向新的文件中写 入数据,这个新的文件比源文件小…
推荐大家,对文件进行操作的时候,按照二进制方式打开文件, 进行操作
size_ t fread(void *ptr, size t size, size t nmemb, FILE *stream);
size. t fwrite(const void *ptr, size. t size, size. t nmemb, FILE stream);
ptr:想要写入文件的数据所在缓冲区首地址/从文件读取到数据索要存放的缓冲区首地址
size: 应该叫block size, 对数据进行操作时候指定的块大小.
nmemb:这个是块个数,实际读写数据的大小应该是 块大小块个数
stream: 文件流指针, fopen返回的文件的操作句柄–通过这 个操作句柄对文件进行各种操作
返回值:实际完整读写的块个数; (假设从文件读取数据, 块大小100,块个数2, 则想要读取200个字节的数据,若文件读取数据读取了完整的200个字节,则返回2,若读取150字节的数据,则第二块不完整,会返回1
read返回值有一个比较特殊的地方: 若读取到文件末尾,没有数据了就会返回0;, 但是实际上若读取文件数据的时候,想要读取块大小100,块个数1,也就是100个字节,但是实际读取不到100字节,则认为这一块不完整,也会返回0;
read返回0,就会造成歧义:到底有没有读取到数据
因此我们推荐,read/wit读写数据的时候,将块大小设置为1,将块个数设置为数据长度;这样就可以消除歧义,都多少数据返回多少, 没有数据就返回0
read返回0,表示的都是读取到文件末尾了 ,没有数据了,但是至于你这次读取了多少数据,我们就没法确定…也就没办法确定要处理多少数据了
int fseek(FILE *stream, long offset, int whence); – 跳转读写位置
每个文件打开后,在文件流指针操作句柄中,都有一一个当前的读写位置指针,这个指针描述符了当前读写位置相对于文件起始位置的偏移量。
因此每次读写数据的时候,都是从这个偏移量位置开始读写的(这个读写指针的位置会随着我们的read/write操作而改变)
文件打开后,除了a/a+之外,其它方式默认的读写位置都在文件的起始位置;
文件的读写指针位置在哪里,fead/wit读写数据就从哪里开始(例外是a/a+. 每次都会写入文件末尾)
stream:文件流指针, fopen返回的操作句柄
offset: 相对于第三个参数whence位置的偏移量
whence: SEEK SET–文件起始位置SEEK _CUR- -文件当前读写位置 /SEEK END- -文件末尾位置
返回值:返回跳转后的位置相对于文件起始位置的偏移量
int flose(ILE *fp); 关闭文件,释放资源;
文件打开后 若不在操作一定要关闭—否则会造成资源泄露
while(a>b)
{
fp=fopen();
fclose(fp);
}
2.系统调用接口的学习
open/ read/ write /lseek /close
int open (char* pathname,int flags,mode_t mode);—并不分文本或者二进制操作,统一按照二进制进行操作
pathname:带有路径的文件名称
flags: 文件的打开方式;
必选其一:O_RDONLY—只读 / O_WRONLY–只写 / O_RDWR–可读可写
可选项:O_CREAT—不存在则创建,存在则打开| O_TRUNC–打开文件同时清空原有内容| O_APPEND—写的时候追加写
r+=O_RDWR w+=O_RDWR| O_CREAT | O_TRUNC
a+=O_RDWR | O_CREAT | O_APPEND
mode:使用了O_CREAT就有可能创建文件,通过mode指定新文件的权限(实际权限= 给定权限mode & (~mask 掩码取反))
返回值:成功返回一个非负整数–叫做文件描述—作为文件的操作句柄; 失败返回-1;
ssize_t write(int fd,const void *buf, size_t count);—向fd所指向的文件中写入buf中的数据, 写入长度是count字节
返回值:成功返回实际写入文件的数据长度; 失败返回-1;
off_t lseek(int fd,off_t offset,int whence);
对于fd指向的文件,读写位置从whence位置开始偏移offset个字节的偏移量
whence: SEEK_SET/ SEEK_CUR /SEEK_END
int close(int fd)–通过文件描述符关闭文件,释放资源
mode _t umask(mode _t mask);—将当前调用进程的文件创建权限掩码设置为mask/ 并不影响shell中的掩码
文件描述符:非负整数–如何作为文件的操作句柄操作文件
常见的文件:标准错误文件–显示器 //标准输出文件—显示器//
标准输入文件—键盘 // 普通文件—键盘
文件描述符实际就是内核中,文件描述信息数组的下标
使用open打开一个文件后,就会创建一个file结构体描述这个文件,并且将这个结构体的首地址添加到进程的files_struct结构体中
fd_array结构体指针数据中,并且返回对应位置的下标给进程,作为文件描述符,作为文件操作句柄。
在进程中通过文件描述符操作文件,通过pcb找到files_struct结构体,在这个结构体中找到fd_array数组。通过fd描述符作为下标,找到对应文件的描述信息指针,进而找到描述信息,进行文件操作。
文件描述符的分配规则:最小未使用
程序运行起来后,进程中默认会打开三个文件—标准输入文件 --0 / 标准输出文件 -1/ 标准错误文件 -2
重定向原理:
我们通过描述符操作一个文件的时候:主要是通过文件描述符,找到文件的描述信息,进而操作文件;
若一个文件描述符这个下标对应的文件描述信息发生改变,则操作这个描述符的时候操作的可就不是源文件了而是新的描述对应的文件
例如:关闭一号标准输出, 1描述符空闲下来,打开新文件,新文件的描述信息指针就会被添加到1号描述符这里;
也就是说,这时候1号描述符,对应的就不是标准输出了,而是新文件;
因为printf操作的是1号描述符,因此原先操作的是标准输出文件显示器,但是发生改变后,就会将要打印的数据写入到了指定文件中
ls 2>>a.txt 将标准输出重定向到a.txt文件, 说白了就是让1号描述符指向新的文件描述信息
重定向原理:通过改变文件描述符对应的文件描述信息 实现改变所操作的文件
int dup2(int oldfd, int newfd);将newfd重定向到oldfd所指向的文件
oldfd是一个文件的描述符
newfd有可能是另一个文件的描述符, 也有可能是空闲的描述符—重定向前会关闭newfd文件
功能:让newfd也指向了oldfd所指向的文件-----让oldfd和newfd都操作oldfd所指向的文件
文件流指针和文件描述符的关系:
文件流指针:FILE*-----库函数的操作句柄
文件描述符:int -----系统调用接口的操作句柄
库函数封装了系统调用接口, 也就是意味着库函数归根结底还是要以系统调用接口完成功能,
也就是意味着库函数的IO接口最终还是要操作文件描述符才能实现文件的IO操作
文件流指针是一个结构体----这个结构体中就包含有一个成员变量—文件描述符
文件系统
磁盘文件管理的系统—超级快 /inode bitmap /data bitmap/ inode /data
inode:inode就是一个文件的描述信息—包含文件的元信息以及文件从磁盘中存储的位置
文件的存储流程/ 文件数据的获取流程
软连接文件和硬链接文件:
本质区别: 软连接文件是独立的 通过保存的源文件路径访问源文件。 硬链接文件与源文件没有什么区别 ,都是文件的名称目录项, 共用同一个inode节点,通过自己的inode节点访问文件数据
动态库与静态库的打包与使用
打包:
1.生成目标文件:gcc -fpIC child.c -o child;
2.将目标文件打包生成库文件:gcc --shared child.o -o libmychild.so / ar -cr libmychild.a child.o
使用:
生成可执行程序连接使用:1.将库文件放到/usr/lib64下 2.添加库文件所在路径到环境变量中 LIBRARY_PATH /3.使用gcc-L 选项指定路径
运行可执行程序加载使用:1.将文件放到/usr/lib64下 /2.添加文件所在路径到环境变量中 LD_LIBRARY_PATH