1.基本概念
文件是在磁盘里永久存储的(因为磁盘是永久性存储介质)。
文件大小为0,依然会在磁盘上占据空间,因为文件=内容+属性
存取,操作都是围绕着内容+属性展开的。
c语言文件接口:C语言文件操作详解-优快云博客
1. 对文件的操作本质上是进程对文件的操作。(操作文件需要先打开文件,进程来打开文件)
2. 磁盘的管理者是操作系统。
3. 文件的读写本质不是通过C/C++的库函数来操作的,这些库函数为用户提供方便。实际是通过文件相关的系统调用接口实现的。(fopen,fclose...底层封装了操作系统的文件调用)。
2.三个默认打开的输入输出流
我们之所以在程序里没有打开任何文件但是能使用键盘写入,使用显示器输出。说明这些都提前被打开了。
任何进程在运行时都会默认打开在C中三个输入输出流分别是stdin(标准输入流),stdout(标准输出流),stderr(标准错误流)。 这三个流的类型都是FILE*,fopen返回值类型,文件指针。
在C++中就是cin,cout与cerr被提前打开。
标准输出流和标准错误流对应显示器,标准输入流对应键盘。
#include<stdio.h>
int main()
{
fputs("hello IO\n",stdout);
fputs("hello IO\n",stdout);
fputs("hello IO\n",stderr);
fputs("hello IO\n",stderr);
return 0;
}
3. 系统文件I/O
(1) open接口
参数及返回值介绍
参数:
1. pathname:要创建或打开的目标文件
2. flags:打开文件时,可以传入多个参数选项,用下面的一个或者多个常量进行 或 运算构成flags。(可用选项如下方表格所示)
3. mode:表示创建文件的默认权限(八进制数)
返回值:
成功:新打开的文件描述符(下个小标题解释)
失败:-1
flags可用选项
选项 | 作用 |
O_RDONLY | 以只读的方式打开文件 |
O_WRONLY | 以只写的方式打开文件 |
O_APPEND | 以追加的方式打开文件 |
O_RDWR | 既能读有能写的方式打开 |
O_CREAT | 如果文件不存在,则创建它。需要使用mode参数,指明新文件的访问权限 |
可以使用逻辑与 | 来同时使用多个打开方式
O_WRONLY|O_CREAT
上面就是表示以只写当时打开文件,如果文件不存在,就创建它。
这种方法本质上是一个宏定义,flags是一个整形,如果一个比特位来作为一个标志位(类似位图)。那flags最多可以传三十二中不同的标志位。
这样通过按位与&操作就可以检测到是否设置了某个选项。
if(flags&O_WRONLY)//检测是否设置O_WRONLY
{
//设置了怎么办
}
if(flags&O_CREAT)//是否设置O_CREAT
{
//设置了怎么办
}
......
若open打开的文件不存在并且flags有O_CREAT文件创键出来的权限还受到umask的影响。(可用umask函数修改umask的值)
我们先来观察一下新创建文件的文件描述符
#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
int main()
{
int fd1=open("st1.txt",O_RDONLY|O_CREAT,0666);
int fd2=open("st2.txt",O_RDONLY|O_CREAT,0666);
int fd3=open("st3.txt",O_RDONLY|O_CREAT,0666);
int fd4=open("st4.txt",O_RDONLY|O_CREAT,0666);
int fd5=open("st5.txt",O_RDONLY,0666);
printf("fd1: %d\n",fd1);
printf("fd2: %d\n",fd2);
printf("fd3: %d\n",fd3);
printf("fd4: %d\n",fd4);
printf("fd5: %d\n",fd5);
return 0;
}
输出结果如下所示
前四个打开成功就返回了文件描述符,我们发现文件描述符是从3开始依次递增的。而文件打开失败就返回了-1。
(2) close接口
系统接口close来关闭指定文件
参数:要关闭文件的文件描述符;
返回值:成功关闭返回0,关闭失败返回-1;
(3) write接口
可以通过write接口对文件进行写入操作,原型为:
ssize_t write(int fd, const void *buf ,size_t const);
参数:
fd是文件描述符buf为用户缓冲区
count为期望写的字节数
返回值:
写入成功返回实际写入的字节数,写入失败返回-1.
#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<string.h>
int main()
{
int fd=open("st.txt",O_WRONLY|O_CREAT,0666);
if(fd==-1)
{
perror("open error:");
return 1;
}
const char* msg="test write^^\n";
for(int i=0;i<10;i++)
{
write(fd,msg,strlen(msg));
}
return 0;
}
输出结果如下所示:
(4) read接口
read也可以对文件进行读写
参数:
fd:文件描述符buf:用户缓冲区
count:希望读的字节数
返回值:
读出成功返回实际读出的字节数,读出失败则返回-1
#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<string.h>
int main()
{
int fd=open("st.txt",O_RDONLY|O_CREAT,0666);
if(fd==-1)
{
perror("open error:");
return 1;
}
char buf[2025]={'\0'};
ssize_t ret=read(fd,buf,2024);
if(ret>=0)
printf("%s\n",buf);
close(fd);
return 0;
}
结果如下:
成功从文件里读出来了。
4. 文件描述符fd
Linux进程默认情况下会有三个缺省打开的文件描述符(这也解释了上面为什么fd从3开始):
标准输入->0
标准输出->1
标准错误->2
fd就是从0开始的整数。
打开文件时操作系统在内存中创建的相应的数据结构来描述目标结构,file结构体(struct file)。表示一个已经打开的文件对象.
每个进程都有一个指针*files指向一张表files_struct,该表最重要的部分就是包含一个指针数组,每一个元素都是一个指向打开文件的指针!本质上文件描述符就是该数组的下标。所以通过文件描述符就可以找到对应的文件。
新打开的文件会在files_struct数组中找到当前没有被使用的最小的一个下标作为新的文件描述符,即关闭了1再打开一个新的文件其fd为1.验证代码如下所示
#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<string.h>
int main()
{
close(0);
close(2);
int fd1=open("st1.txt",O_RDONLY|O_CREAT,0666);
int fd2=open("st2.txt",O_RDONLY|O_CREAT,0666);
int fd3=open("st3.txt",O_RDONLY|O_CREAT,0666);
int fd4=open("st4.txt",O_RDONLY|O_CREAT,0666);
int fd5=open("st5.txt",O_RDONLY,0666);
printf("fd1: %d\n",fd1);
printf("fd2: %d\n",fd2);
printf("fd3: %d\n",fd3);
printf("fd4: %d\n",fd4);
printf("fd5: %d\n",fd5);
return 0;
}
我们将默认打开的标准输入和标准错误给关闭了,
这一篇就到这里了(๑′ᴗ‵๑)I Lᵒᵛᵉᵧₒᵤ❤