标准库IO接口:
一般程序运行起来,自动默认打开 标准输入文件 fd=0(scanf),标准输出文件 fd=1(printf), 标准错误文件 fd=2
- fopen 打开文件
FILE* fopen(带路径的文件名,打开方式:"r r+ w w+ a a+ b")
r:只读 若文件不存在报错返回
r+:可读可写 若文件不存在报错返回
w:只写,若文件不存在则创建,若文件存在,清空文件原有内容
w+:可读可写,若文件不存在则创建,若文件存在,清空文件原有内容
a:追加只写 每次写入数据追加到文件末尾,若文件不存在则创建
a+:可读 或 追加写入 每次写入数据追加到文件末尾,若文件不存在则创建
b:对文件数据进行原始二进制操作
返回值:成功:返回操作句柄:FILE*文件流指针 失败:返回NULL
- fwrite 写文件
size_t fwrite(写入文件数据(const void*ptr),size(块大小),nmemb(块个数)文件流指针)
块大小*块个数=写入总长度
返回值:完整写入文件的块个数 失败返回0
- fread 读文件
size_t fread(读取到数据的缓冲区地址(void*ptr),size(块大小),nmemb(块个数),文件流指针)
块大小*块个数=读取总长度
返回值:完整读取文件的块个数 设置块大小为长度 块个数为1 但不确定是否读入 可以设置块大小为1 快个数为一个数 根据返回块个数可以确定是否数据有读入 失败返回0或者读取到文件末尾也返回0
- fseek 跳转文件读写位置
int fseek(文件流指针,偏移量,偏移的相对起始位置(SEEK_SET:起始位置;SEEK_CUR:当前位置 SEEK_END:末尾))
成功返回0 错误返回-1
- fclose 关闭文件
代码演示
结果演示:
系统调用IO接口:
- open 打开文件
int open(文件路径名,标志位(类似于打开方式),文件权限)
标志位:必选项:O_RDONLY,O_WRONLY,O_RDWR
可选项:O_CREAT:如果文件不存在则创建
O_EXCL:如果文件已经存则报错返回
O_TRUNC:打开文件时截断文件长度为0 即清空内容
O_APPEND:将写入设置为追加写
使用过程中 将几个标志位通过‘|’(或操作)连接使用
文件权限前面要加0 如 0777 如果使用了O_CREAT就必须要指定文件权限 注意:此处会受到系统umask掩码影响 给定权限mode&~umask为实际权限
解决方法:在#include<sys/stat.h>调用umask(0)函数 设置当前程序掩码为0
返回值:成功返回非负数(操作句柄) 失败返回-1
- write
size_t write(open返回的操作句柄,要写入的数据缓存区buf,要写入的数据长度)
返回值:成功返回实际写入文件数据的长度 失败返回-1
- read
size_t read(open返回的操作句柄,读取到数据缓冲区buf,要读取的数据长度)
返回值:成功返回实际读取到的数据的长度 失败返回-1
- lseek
off_t fseek(open返回的操作句柄,偏移量,偏移的相对起始位置(SEEK_SET:起始位置;SEEK_CUR:当前位置 SEEK_END:末尾))
返回值:跳转后的位置相对于起始位置的偏移量(设置文件末尾 用于获取文件长度) 错误返回-1
- close
int close(open返回的操作句柄)
代码示例:
结果演示:
综上所述:库函数IO与系统调用IO功能类似,为什么还要对系统调用接口进行封装呢?
首先要理解open返回的操作句柄 即文件描述符--一个非负数 是如何描述一个具体文件呢?
文件描述符 为系统内核中进程所打开的文件描述信息表中的下标索引
文件描述符分配原则:在file_struct数组中,找到未被使用的最小下标,作为新的文件描述符
标准输入文件 fd=0(scanf),标准输出文件 fd=1(printf), 标准错误文件 fd=2
引入重定向概念:通过改变文件描述符下标位置所保存的文件描述信息,进而改变这个描述符所操作的文件,改变数据的流向
int dup2(int oldfd,int newfd)将newfd重定向到oldfd
文件流指针就是一个FILE*结构体指针,结构体中包含一个成员变量就是文件描述符
具体原理参照下图
库函数io与系统调用IO区别:
缓冲区:即换行刷新缓存区 是文件流指针所有的,对于文件描述符或系统调用来说是没有的
文件流指针的缓冲区被称之为:用户态缓冲区(内核空间+用户空间 一般所开辟的内存空间都在用户空间中)