基础IO:IO接口的基本认识与使用,文件描述符与重定向,静态库与动态库的生成与使用
函数IO接口: fopen fwrite fread fseek fclose
系统调用IO接口:
库函数是对系统调用接口的封装
接口: open write read lseek close
int open(char *pathname , int flag , int mode);
pathname: 要打开的文件路径名
flag: 文件的打开方式,打开方式决定了能够对文件进行什么样的操作
必选项:O_RDONLY O_WRONLY O_RDWR 但是只能选其一,不能同时使用
可选项:O_CREAT 文件不存在则创建
O_TRUNC 截断文件,丢弃原有数据
O_APPEND 追加写
如 w+(可读可写,文件已经存在则截断内容为0) —— O_RDWR|O_CREAT|O_TRUNC
mode:当O_CREAT被使用时,就一定要设置第三个参数mode——用于设定被创建文件的访问权限0664
0664——特别需要注意,0不能被省略,会涉及到特殊权限位的设置
给定的权限不一定是实际得到的权限,实际的文件权限会受到umask的影响
实际的权限 = 给定的权限 & (~umask)
mode_t umask(mode_t mask); 用于设置调用进程的权限掩码
返回值:成功返回一个文件描述符(非负整数)作为文件的操作句柄;
失败返回-1
ssize_t write(int fd, char *buf, size_t len);
fd: open打开文件时返回的操作句柄——文件描述符
buf:要写入文件的数据的所在空间首地址
len:要写入的数据长度(以字节为单位)
返回值:成功返回实际写入的数据长度:
失败返回-1;
ssize_t read(int fd, char *buf, size_t len);
fd:open打开文件时返回的文件描述
buf:一块空间的首地址,用于存放读取到的数据
len:要读取的数据长度(这个长度不能大于buf空间的大小,避免越界)
返回值:成功返回实际读取到的数据长度(字节为单位)
0表示读取到了文件末尾
-1表示失败
off_t lseek(int fd, off_t offset, int whence);
fd:文件描述符
offset:偏移量
whence:偏移其实位置 SEEK_SET; SEEK_CUT; SEEK_END;
返回值:当前跳转后,读写位置相对于文件其实位置的偏移量(接口有另一种另类的用法,跳转到末尾,通过返回值确定文件大小)
int close(int fd);
fd:文件描述
#include <stdio.h>
#include <fcntl.h>
#include <string.h>
#incldue <unistd.h>
#include <sys/stat.h>
int main()
{
umask(0);//将当前调用进程的文件权限创建掩码设置为0,这样open给定的权限就是实际创建得到的权限
int fd = open("./tmp.txt", O_CREAT|O_RDWR|O_TRUNC, 0777);//110110100
if(fd < 0)
{
perror("open error");
return -1;
}
char *data = "天黑了";
ssize_t ret = write(fd, data, strlen(data));
if (ret < 0)
{
perror("write error");
close(fd);
return -1;
}
lseek(fd, 0, SEEK_SET);
char buf[1024] = {0};
ret = read(fd, buf, 1023);//成功返回实际读到的数据长度,返回0表示到达文件末尾没有读到数
//据,-1表示出错
if(ret < 0)
{
perror("read error");
close(fd);
return -1;
}
printf("%s", buf);
close(fd);
return 0;
}
因为系统调用接口功能比较单一,在某些特定的场景下用起来比较麻烦,因此做了进一步的操作:库函数是对系统调用接口的封装
printf将数据写入显示器文件,但是不单单是写入,还包含了数据的格式化(将多个数据按照指定格式组织成为一个字符串再写入)
库函数fopen, fwrite, fread, fclose都是对系统调用接口open, write, read, close接口的封装
封装的目的是什么?
FILE *fopen(char *pathname...); fwrite(char *buf, int size...) FILE *fp是操作句柄
int open(char *pathname...); write(int fd, char *buf,int len) int fd是操作句柄
fwrite是个库函数,本质上内层肯定要调用系统调用接口write,则需要传入一个文件描述符
注意:封装不仅仅是接口,还有操作句柄,实际上FILE这个结构体中就包含了文件描述符成员
FILE结构体本身就是对文件描述符的一层封装
通常说,向文件写入数据,数据并不会立即被写入文件,而是先写入缓冲区,等刷新缓冲区的时候写入文件,对于系统调用接口来说,本质上是没有这个缓冲区的,因此write向显示器写入数据是直接打印的;而printf,fprintf,fwrite是刷新缓冲区的时候才打印
_exit和exit都是退出进程,区别在于是否刷新缓冲区,其实我们所说的缓冲区,对于系统调用接口来说根本就是不存在,因此系统调用接口_exit退出前就没有刷新缓冲区这么一说,最基础的文件流结构针对文件描述符做的一个改进:添加缓冲区
文件描述符:
文件描述符:被打开的文件的操作句柄——是一个非负整数
文件描述符,本质上是一个数组下标,对应的数组保存的是进程打开的文件的 描述信息,当操作文件的时候需要传入描述符,其实就是通过描述符这个下标找到 对应的文件描述信息,进而操作文件
1. 当我们打开了一个文件,涉及到对对应设备的操作
2. 用一个结构将这个设备描述起来(磁盘文件,就是要描述操作时哪一块硬盘的多少数据,以及权限)一个进程中可能会打开很多文件,这些描述信息就需要组织起来(本质使用了一个数组)

被折叠的 条评论
为什么被折叠?



