【Linux】浅谈文件原理与操作 | 您所在的位置:网站首页 › 打开文件操作文件关闭文件c语言 › 【Linux】浅谈文件原理与操作 |
目录 问题引入 浅谈文件原理 文件操作 文件的打开与关闭 open close write与read 再谈C库文件操作 问题引入🌸以前我们学过C语言的文件操作,而不同语言的文件操作都是不一样的,我们该如何理解这一现象,能不能用一种统一的视角看待所有文件操作?今天就一起来谈谈文件操作。 浅谈文件原理我们都知道,文件 = 内容 + 属性,对于一个文件的操作可以看成对内容或属性的操作。 通过冯诺依曼体系我们知道当我们对文件进行操作时,该文件一定处于内存之中,而未被进行操作的文件则位于磁盘里。 在操作系统看来,文件便可以被分成两大类:磁盘文件和被打开文件。 🌸其中被打开文件至少需要将文件属性加载到内存之中,而每次文件操作前我们都要先打开文件的本质就是:将目标文件属性加载到内存中。因此,OS内部一定会同时存在大量的被打开的文件。需要将其以先描述再组织的方式管理起来。 这样在OS内部对于打开文件的管理就转换成了对链表的增删查改。即文件被打开前,OS要为被打开文件创建内核数据结构 struct file。 🌸那么文件是被谁打开的呢? 用户通过调用进程完成打开文件的操作如此解释后,我们对于文件又有进一步的认识,下面我们来介绍一下在OS层面文件操作的系统接口。 文件操作🌸对C语言文件操作不熟悉或有些忘记了的,也可以看看这篇复习一下:C语言文件操作的基础应用。 文件的打开与关闭 open🌸通过手册查询,我们可以找到 open 函数的相关信息,其中的参数分别是文件路径和打开文件的选项。 🌸其中的 flag ,我们不能将其看成一个整数而是将其看作一个位图。 🌸其中每一个位置都代表一个选项,为 1 则表示选中反之不选,库中定义了宏方便我们直接使用。 O_WRONLY 只写选项O_RDONLY 只读选项O_APPEND 追加写入O_TRUNC 打开时清空文件O_CREAT 若文件不存在则创建O_RDWR 支持同时读写🌸在使用时使用 | 操作符标记位图的对应位置即可。 int main() { open("log.txt", O_WRONLY | O_TRUNC | O_CREAT); //以只读每次打开清空文件若不存在则创建新文件的方式打开 return 0; }🌸这时候,我们便会发现现在的这个操作跟 fopen 十分的相似,只不过将 w 选项拆成了三个选项。其实,fopen 的底层便是调用了这个系统调用进行文件的打开操作。 🌸虽然我们成功创建出一个文件了,但此时我们可以看到,该文件的权限却是乱码,因此在创建文件时我们就需要手动设置文件的权限。 🌸open 还重载了三个参数的版本,供我们设置文件权限。 int main() { open("log.txt", O_WRONLY | O_TRUNC | O_CREAT,0666); return 0; } 🌸这下创建出来的就是正常权限的文件了,但是却与我们设置的 0666 不一样而是 0664 呢?这是由于文件的权限还受到权限掩码的影响,因此需要将 umask 置零。 int main() { umask(0); open("log.txt", O_WRONLY | O_TRUNC | O_CREAT,0666); return 0; }🌸这下就根据我们要求创建出对应权限的文件了。 🌸而 open 有一个返回值,我们刚才忽略掉了,其为文件描述符又名 fd,我们需要定义一个变量接收一下它,之后需要用它定位文件。 🌸若 fd 为 -1 说明打开文件失败,我们可以在打开文件后增加一个判断。 int main() { umask(0); int fd = open("log.txt", O_WRONLY | O_TRUNC | O_CREAT,0666); if(fd == -1) { printf("fd: %d,errstring is: %s\n",fd,strerror(errno)); } return 0; } close🌸关闭文件就需要我们上面存起来的 fd 了,直接传入 fd 即可关闭文件。 int main() { umask(0); int fd = open("log.txt", O_WRONLY | O_TRUNC | O_CREAT,0666); if(fd == -1) { printf("fd: %d,errstring is: %s\n",fd,strerror(errno)); } close(fd); return 0; } write与read🌸打开文件后我们就可以根据需求对文件进行读写了,先介绍 write 接口。 🌸参数分别是 fd,写入内容的字符串和写入的字节数,写入后返回写入的字节数,失败则返回 -1。 🌸这里我先将用 snprintf 内容格式化写入一个字符串中,再写到文件里。 int main() { umask(0); int fd = open("log.txt", O_WRONLY | O_TRUNC | O_CREAT,0666); if(fd == -1) { printf("fd: %d,errstring is: %s\n",fd,strerror(errno)); } const char* ptr = "helloworld"; //原字符串 char str[20]; //转接的字符数组 int i = 1; snprintf(str,sizeof(str),"%s: %d",ptr,i); //加个序号格式化写入 write(fd,str,strlen(str)); //写入文件 close(fd); //关闭文件 return 0; }[注意]:\0 是C语言库中的设定,直接写入到文件中会被识别成乱码,使用 write 写入时要注意不写入 \0。因此写入时直接使用 strlen 便可以自动规避掉 \0。 🌸之后我们来看读取的操作,需要传入 fd 定位文件,之后将文件内的数据读入数组中,读取 count 个字节。 🌸成功读取后会返回读取的字节数,若读取失败则会返回 -1。 🌸这次我们以读取的方式打开文件,由于不用创建文件因此就不需要手动设置文件权限了。 int main() { int fd = open("log.txt", O_RDONLY); //只读打开文件 char buffer[1024]; size_t n = read(fd, buffer, sizeof(buffer) - 1); //为\0留一个位置 if (n > 0) //说明读入数据 { buffer[n] = '\0'; //在数据尾部插入\0 printf("%s\n", buffer); //打印出数据内容 } return 0; }🌸同时为了方便接下来的读写,读取需要为 \0 预留一个位置,之后再加上即可。 再谈C库文件操作🌸在上面我们就说过C库文件操作的底层本质上也是调用了操作系统提供的系统调用,即库函数只是对系统调用的封装。 🌸那又是为什么呢?我们可以用下面这张图简单理清几者之间的关系。 🌸只要涉及文件操作,就与文件读写分不开关系,在读写的过程中便会涉及到对硬件的访问,而硬件是由操作系统进行管理的。因此从用户层面出发,只要访问到硬件就无法绕开操作系统。 🌸不仅只是C语言,任何语言的文件操作都是对系统调用的封装,都是借助系统接口才能够实现!!! 🌸好了,今天 浅谈文件原理与操作 的相关内容到这里就结束了,如果这篇文章对你有用的话还请留下你的三连加关注。 |
CopyRight 2018-2019 实验室设备网 版权所有 |