1.什么是文件IO
1.1概念:
文件IO又称为系统IO,是系统调用,是操作系统提供的接口函数。
如图:标准IO:你的程序在在运行的时候,如果想要读取磁盘内容,就要使用C库函数接口,然后通过内核的系统调用取去驱动程序把磁盘文件传输给你的程序;如果这个时候你使用文件IO,就不再调用C库函数接口,而是直接使用系统调用接口,速度上是快了很多,但是也是有别的缺点(后面说)。
1.2 特点
- 没有缓冲机制,每次调用都会引起系统调用,(标准库中的函数会在用户空间维护一个缓冲区,当程序写入数据时,先存储到用户空间的缓冲区,等缓冲区满了或满足特定条件时,才通过系统调用一次性将数据提交到内核,减少系统调用的次数,提高效率。)
- 围绕文件描述符进行操作,非负整数(>=0),依次分配
- 文件IO默认打开三个文件描述符,分别是0(标准输入),1(标准输出),2(标准错误)(和标准IO的流类似)
- 操作除了目录以外的任意类型的文件: b c - l s p(七个文件类型)
- 可移植性相对较弱(就是跨平台能力,因为直接使用系统调用,不同的操作系统的系统调用函数是不同,所以你的程序到别的操作系统可能就跑不了)
思考:
1.一个进程的文件描述符最大到几?最多能打开多少个文件描述符?最多能打开多少个文件?
答:一个进程的文件描述符最大到1023(0-1023) ,最多能打开1024个文件描述符, 最多能打开1021(1024-3)个文件。
2.如果我打开三个文件,描述符分别是: 3 4 5
关闭3和5以后, 再打开一个别的文件,描述符是几?
答:是3(从最小开始分配)
2.函数接口
2.1打开关闭文件open()和close()
2.1.1 open()
头文件:#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>int open(const char *pathname, int flags);
功能:打开文件
参数:pathname:文件路径名
flags:打开文件的方式
O_RDONLY:只读
O_WRONLY:只写
O_RDWR:可读可写
O_CREAT:不存在创建
O_TRUNC:存在清空
O_APPEND:追加
返回值:成功:文件描述符
失败:-1
重点:当第二个参数中有O_CREAT选项时,需要给open函数传递第三个参数,指定创建文件的权限(就是如果你是第一次创建文件(这个文件不存在),那么必须给这个文件添加权限(flags),并且以你第一次创建给它的权限为准,权限不会再通过这个函数而改变除非你删除或者终端使用chmod改变权限)
int open(const char *pathname, int flags, mode_t mode);
最后权限 = mode & (~umask)
例如:指定的权限0666(8进制)
最终权限: 0666 & (~umask) = 0666 & (~0002)=0666 & 0775 = 0664
注意:通过umask命令可以查看umask文件权限掩码
文件IO和标准IO的打开方式的对应关系
标准IO |
文件IO |
r |
O_RDONLY 只读 |
r+ |
O_RDWR 可读可写 |
w |
O_WRONLY | O_CREAT|O_TRUNC, 0777 只写,不存在创建,存在则清空 |
w+ |
O_RDWR | O_CREAT|O_TRUNC, 0777 可读可写,不存在创建,存在则清空 |
a |
O_WRONLY | O_CREAT| O_APPEND, 0777 只写,不存在创建,存在追加 |
a+ |
O_RDWR | O_CREAT| O_APPEND, 0777 可读可写,不存在创建,存在追加 |
2.1.2 close()
头文件:#include <unistd.h>int close(int fd);
功能:关闭文件
参数:fd:文件描述符
例:
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int main(int argc, const char *argv[])
{
int file = open("2",O_RDONLY,0666); //打开文件
if(file < 0){ //判断是否成功打开
perror("open err");
return -1;
}
printf("fd:%d\n",file); //打印文件标识符(3)
file = close(file); //关闭文件
printf("fd:%d\n",file);
return 0;
}
2.2 读写文件read()和write()
头文件:#include <unistd.h>
ssize_t read(int fd, void *buf, size_t count);
功能:从一个已打开的可读文件中读取数据
参数: fd 文件描述符
buf 存放位置
count 期望的个数
返回值:成功:实际读到的个数(小于期望值说明实际没这么多)
返回0,表示读到文件结尾
失败:返回-1:表示出错,并设置errno号
ssize_t write(int fd, const void *buf, size_t count);
功能:向指定文件描述符中,写入 count个字节的数据。
参数:fd 文件描述符
buf 要写的内容
count 期望写入字节数
返回值:成功:实际写入数据的个数
失败 : -1
2.3 文件定位操作 lseek()
off_t lseek(int fd, off_t offset, int whence);
功能:设定文件的偏移位置
参数:fd:文件描述符
offset: 偏移量
正数:向文件结尾位置移动
负数:向文件开始位置
whence: 相对位置
SEEK_SET 开始位置
SEEK_CUR 当前位置
SEEK_END 结尾位置
补充:和fseek一样其中SEEK_SET,SEEK_CUR和SEEK_END和依次为0,1和2.
返回值:成功:文件的当前位置
失败:-1
例:
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int main(int argc, const char *argv[])
{
int fd;
fd = open("2",O_RDWR|O_CREAT|O_TRUNC); //可读可写,如果不存在创建存在清空(w+)
if(fd < 0){
perror("open err");
return -1;
}
char buf[30] = "";
write(fd,"hello\n",6); //写入
write(fd,"hello\n",6);
write(fd,"hello",5);
lseek(fd,0,SEEK_SET); //定位到文件开头
read(fd,buf,sizeof(buf)); //读取
printf("%s\n",buf); //打印读取的内容
return 0;
}
练习:
文件IO实现cp功能。cp 源文件 新文件名
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
int main(int argc, const char *argv[])
{
if(argc != 3){
perror("argc err");
return -1;
}
int fd = open(argv[1],O_RDWR);
if(fd < 0){
perror("open1 err");
return -1;
}
int fdnew = open(argv[2],O_RDWR|O_CREAT|O_TRUNC,0666); //打开新文件(w+)
if(fdnew < 0){
perror("open2 err");
return -1;
}
char buf[30] = "";
while(read(fd,buf,1) != 0){ //一个字符一个字符读取
write(fdnew,buf,1); //一个字符一个字符输出到新文件
}
close(fd);
close(fdnew);
return 0;
}
标准IO和文件IO总结
标准IO |
文件IO | |
概念 |
C库中定义一组用于输入输出的函数 |
posix中定义的一组用于输入输出的函数 |
特点 |
|
|
函数 |
打开文件:fopen 关闭文件: fclose 读文件:fgets/fread 写文件:fputs/fwrite 定位操作:rewind/fseek/ftell |
打开文件:open 关闭文件:close 读文件: read 写文件:write 定位操作:lseek |