一、文件IO
(一)概念
文件IO就是系统调用,用户空间进入内核空间的过程就是系统调用。
与标准IO相比,系统调用没有缓冲机制,效率较低,可移植性也相对较差,但是实时性高。
文件描述符 是使用open函数打开文件时的返回值,一般叫做fd,这个fd就代表这个打开的文件,以后对文件的读写操作,就是通过这个文件描述符fd来完成的。
fd是一个整数,一般默认情况下,一个程序中文件描述符的范围0-1023共计1024个,
使用ulimit -a
可以查看一个程序中可以打开的文件的个数限制
![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/530eb6b3eab64179b5c4ba1032dbd54e.png)
(open files 后面对应的就是 这个值也可以使用命令ulimit -n 2048
来修改 但一般都是用默认值)
在一个程序启动的过程中,默认就会打开三个描述符(0 1 2)。
如果再打开新文件,一般是从3开始的。
(二)open和close 函数的使用
1. open
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int open(const char *pathname, int flags, mode_t mode);
功能:
使用文件IO的方式打开一个文件
参数:
pathname:文件的路径和名字
flags:
O_RDONLY 只读的方式打开文件
O_WRONLY 只写的方式打开文件
O_RDWR 读写的方式打开文件
----上面三个必须三选一 下面的是附加选项----
O_APPEND 以追加的方式打开文件
O_CREAT 如果文件不存在,则新建这个文件
如果指定了这个宏,则第三个参数 mode 必须填
mode代表的是创建文件的权限
创建普通文件的最大权限为 0666
也就是说我们给mode赋值 最大应该就为 0666
但是实际上创建文件的权限还得涉及 掩码 umask
umask的值 默认为 0002 这个值也可以改的
最终的权限 = (mode & ~umask)
所以 即使给的是 0666 最终的权限也是 0664
O_EXCL 需要和 O_CREAT 一起使用 如果文件不存在会创建文件
如果文件存在,会报错 错误码:EEXIST
O_TRUNC :如果文件存在就清空
mode:代表文件的权限 一般用8进制表示
mode_t 即 unsigned int
返回值:
成功 文件描述符(返回的文件描述符遵循未被打开的最小原则)
失败 -1 重置错误码
(2)flags操作的原理
标准io | 文件io | 说明 |
---|
r | O_RDONLY | 以只读的方式打开文件 |
r+ | O_RDWR | 以读写的方式打开文件 |
w | O_WRONLY|O_CREAT|O_TRUNC, 0666 | 以只写的方式打开文件,文件存在就清空不存在就新建 |
w+ | O_RDWR|O_CREAT|O_TRUNC, 0666 | 以读写的方式打开文件,文件存在就清空不存在就新建 |
a | O_WRONLY|O_APPEND|O_CREAT,0666 | 以结尾写的方式打开文件,不存在就新建 |
a+ | O_RDWR|O_APPEND|O_CREAT,0666 | 以读和结尾写的方式打开文件,不存在就新建 |
2. close 函数的使用
#include <unistd.h>
int close(int fd);
功能:关闭文件
参数:fd:文件描述符
返回值:
成功 0
失败 -1 重置错误码
函数 | open | close |
---|
头文件 | sys/types.h、sys/stat.h、fcntl.h | unistd.h |
参数 | char *pathname, int flags, mode_t mode | int fd |
返回值 | int 成功返回文件描述符;失败返回-1,重置错误码 | int 成功返回0,失败返回-1,重置错误码 |
功能 | 打开文件 | 关闭文件 |
备注 | 返回的文件描述符遵循未被打开的最小原则 | |
(三)read/write函数
1. read
#include <unistd.h>
ssize_t read(int fd, void *buf, size_t count);
功能:
从文件fd中读取最多count个字节到buf中
参数:
fd:文件描述符
buf:用来存放读到的内容的缓冲区的首地址
count:想要读取的字节数
ssize_t 和 size_t 即 long
返回值:
成功 实际读到的字节数(读到文件结束会返回0)
失败 -1 重置错误码
2. write
#include <unistd.h>
ssize_t write(int fd, const void *buf, size_t count);
功能:
把buf指向的内容写最多count个到文件fd中
参数:
fd:文件描述符
buf:要写入的内容的首地址
count:想要写入的字节数
返回值:
成功 实际写入的字节数
失败 -1 重置错误码
函数 | read | write |
---|
头文件 | unistd.h | unistd.h |
参数 | int fd, void *buf, size_t count | int fd, void *buf, size_t count |
返回值 | ssize_t 实际读取的字节数,读到结尾返回0;失败返回-1,重置错误码 | ssize_t 实际写入的字节数,失败返回-1,重置错误码 |
功能 | 从文件fd中读取最多count个字节到buf指向的内存中 | 将buf中最多count个字节写入到文件fd中 |
3. 使用示例
功能需求:使用write和read复制文件,要求从命令行传参
需求分析:
read函数会返回实际读取的字节数。
代码实现:
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
int main(int argc, char const *argv[])
{
if(3 != argc){
printf("Usage:./a.out src dest\n");
exit(-1);
}
int src_fd = open(argv[1],O_RDONLY);
int dest_fd = open(argv[2],O_RDWR|O_CREAT|O_TRUNC,0666);
char buff[10]={0};
int nbyte=0;
while(0 < (nbyte=read(src_fd,buff,sizeof(buff)))){
if(nbyte != write(dest_fd,buff,nbyte))
perror("write error");
}
return 0;
}
(四)光标操作
#include <sys/types.h>
#include <unistd.h>
off_t lseek(int fd, off_t offset, int whence);
功能:
设置光标位置
参数:
fd:文件描述符
offset:偏移量
0 不偏移
>0 向后偏移
<0 向前偏移
off_t 即 long
whence:相对位置
SEEK_SET 相对于文件开头
SEEK_CUR 相对于当前位置
SEEK_END 相对于文件结尾
返回值:
成功 光标新位置距离文件开头的偏移量
失败 -1 重置错误码
使用实例:
lseek(fd, 0, SEEK_SET);
lseek(fd, 0, SEEK_END);
lseek(fd, 10, SEEK_CUR);
lseek(fd, -10, SEEK_CUR);
lseek(fd, 10, SEEK_SET);
lseek(fd, -10, SEEK_END);
二、文件IO和标准IO的区别
(一)open和fopen
| 文件IO | 标准IO |
---|
函数 | open | fopen |
功能 | 以文件IO的方式打开文件 | 以标准IO的方式打开文件 |
参数 | char *pathname,int flags,mode_t mode | char *pathname,char *mode |
返回值 | int 成功返回fd(遵循未被打开的最小原则),失败返回-1,重置错误码 | FILE* 成功返回文件指针;失败返回NULL,重置错误码 |
(二)close和fclose
| 文件IO | 标准IO |
---|
函数 | close | fclose |
功能 | 关闭文件 | 关闭文件 |
参数 | int fd | FILE *fp |
返回值 | 成功,0;失败,-1,重置错误码 | 成功,0;失败,EOF,重置错误码 |
(三)read和fread
| 文件IO | 标准IO |
---|
函数 | read | fread |
头文件 | unistd.h | stdio.h |
功能 | 从文件fd中读取最多count个字节到buf指向的内存中 | 在文件stream中读取nmemb个size大小的项目到ptr指向的内存中 |
参数 | int fd, void *buf, size_t count | void *ptr, size_t size, size_t nmemb, FILE *stream |
返回值 | ssize_t 实际读取的字节数,读到结尾返回0; | size_t 成功,实际读到的项目数;失败,小于项目数的数(或者0) |
- 注:
- fread中,关于size参数,是因为ptr类型为void*,因此由size来控制单个项目的字节数
- fread不会区分文件尾还是出错,因此需要再进一步使用feof和ferror两个函数来判断是到达了文件尾还是出错了。
补充:标准IO中fgets和fread比较
| fgets | fread |
---|
功能 | 从文件stream中读取最多 size-1 个字节到s指向的缓冲区中,遇到EOF或者’\n’会停止读取(‘\n’也会被读到buff中 且在结尾加一个’\0’) | 在文件stream中读取nmemb个size大小的项目到ptr指向的内存中 |
参数 | char *s, int size, FILE *stream | void *ptr, size_t size, size_t nmemb, FILE *stream |
返回值 | char* 成功,s的首地址;文件结束或者出错,NULL | size_t 成功,实际读到的项目数;失败,小于项目数的数(或者0) |
(四)write和fwrite
| 文件IO | 标准IO |
---|
函数 | write | fwrite |
头文件 | unistd.h | stdio.h |
参数 | int fd, void *buf, size_t count | void *ptr, size_t size, size_t nmemb, FILE *stream |
返回值 | ssize_t 实际写入的字节数,失败返回-1,重置错误码 | size_t 成功,实际写入到的项目数;失败,小于项目数的数(或者0) |
功能 | 将buf中最多count个字节写入到文件fd中 | 把ptr指向的空间中的内容写入nmemb个size大小到文件stream中 |
补充:标准IO中fputs和fwrite比较
| fputs | fwrite |
---|
功能 | 将字符串s写入文件stream中 | 把ptr指向的空间中的内容写入nmemb个size大小到文件stream中 |
参数 | char *s,FILE * stream | const void *ptr, size_t size, size_t nmemb, FILE *stream |
返回值 | 成功,非负数;失败,EOF(-1) | 成功 实际写入到的项目数;失败 小于项目数的数(或者0) |
(五)fseek和lseek
| 文件IO | 标准IO |
---|
函数 | lseek | fseek |
头文件 | sys/types.h、unistd.h | stdio.h |
参数 | int fd, off_t offset, int whence | FILE *stream, long offset, int whence |
返回值 | 成功时返回新位置距离文件开头的偏移量,失败返回-1,重置错误码 | 成功返回0;失败返回-1,重置错误码 |