鹤壁市网站建设,设计师网址导航优缺点,公益网站 做公益赚钱,vue 做自适应网站#x1f31f;hello#xff0c;各位读者大大们你们好呀#x1f31f; #x1f36d;#x1f36d;系列专栏#xff1a;【Linux初阶】 ✒️✒️本篇内容#xff1a;重新理解文件和文件操作#xff0c;C语言实现的简单文件操作#xff0c;文本初始权限#xff0c;系统接口介… hello各位读者大大们你们好呀 系列专栏【Linux初阶】 ✒️✒️本篇内容重新理解文件和文件操作C语言实现的简单文件操作文本初始权限系统接口介绍open、close、write、read 作者简介计算机海洋的新进船长一枚请多多指教( •̀֊•́ ) ̖́- 文章目录 前言一、重新谈论文件二、重新谈论文件操作1.除了C/C其他语言有文件接口吗2.vim知识补充 - 如何批量注释代码3.C语言实现的简单文件操作1写入文件2读取文件3追加文件内容 三、文本初始权限四、系统接口介绍1.open接口2.open接口中的flag标记位1flag标记位介绍2flag底层原理仅作了解 3.close4.write接口5.read接口6.库函数接口和系统调用接口的关系 结语 前言
相信很多朋友在C语言学习阶段已经接触过文件操作的相关知识在这篇文章中我们会更深入的学习文件的知识以操作系统的视角重新认识和理解文件。
那么话不多数让我们直接开始吧 一、重新谈论文件
在学习文件之前我们需要重新回顾一下文件的相关知识让我们对文件有一个系统、立体的认识。
空文件也要占据空间文件 内容 属性文件操作 对内容/属性 or 内容属性的操作标定一个文件必须使用文件路径文件名【唯一性】如果没有指明对应的文件路径默认是在当前路径下进行文件访问当我们把 fopenfclosefreadfwrite等接口写好代码编译完形成二进制可执行文件之后如果没有运行起来文件对应的操作不会被执行文件操作的本质进程对文件的操作一个文件要被访问就必须先被打开用户进程调用接口OS帮我们打开 所以我们研究文件操作实际上就是在研究进程和被打开文件的关系 二、重新谈论文件操作
1.除了C/C其他语言有文件接口吗
实际上除了C/CJava、Python、php、go等语言都有自己的文件操作接口它们的文件操作接口都不一样那么问题来了有那么多的文件操作接口我们应该怎么降低我们的学习成本呢
在解答上面这个问题之前我们要明确我们的文件是如何被操作的。我们的文件储存在我们的磁盘中磁盘 - 硬件 - OS硬件被操作系统管理-所有人想访问磁盘都不能绕过OS - 使用OS提供的接口文件级接口。
所以无论上层语言怎么变化a.库函数底层必须使用系统调用接口b.库函数可以千变万化但是底层不变。这就得出了我们降低学习成本问题的答案只要我们学习不变的东西我们就可以降低我们学习文件操作知识的成本了。在这篇文章中我会着重讲述文件操作的底层原理和常见系统调用接口。
———— 我是一条知识分割线 ————
2.vim知识补充 - 如何批量注释代码
在博主使用 vim进行学习的过程中常常需要对其中的代码进行大范围操作经过网络搜索发现各种阅读量靠前的信息并不能满足自己对 vim操作快速便捷简明的要求因此在此处根据自己的编码习惯对 vim相应的知识做相应的补充。
批量化注释代码Ctrlv进入块选择模式V-BLOCK模式h、j、k、l分别代表左下上右控制块的大小输入大写 I此时下方会提示进入“insert”模式再输入注释符//按Esc回到初始NORMAL模式返回后才会完成批量化注释。
撤销上一次的操作初始NORMAL模式下按 u。
取消批量注释Ctrlv进入块选择模式V-BLOCK模式选中你要删除的行首的注释符号注意// 要选中两个输入d 即可完成取消批量注释。
调整代码格式 批量删除代码Ctrlv进入块选择模式V-BLOCK模式下拉选中你要删除的代码首行输入小写 d代码集体向前缩进一行。输入大写 D删除选中行所有的代码。
———— 我是一条知识分割线 ————
3.C语言实现的简单文件操作
1写入文件
打开文件的操作如下fopen(要打开的文件名, 操作形式)其中操作形式有 r读、w写、r、w、a追加文件内容、a 等。 //r,w, r(读写,不存在出错),w(读写, 不存在创建), a(append, 追加), a()【追加式写入】FILE* fp fopen(FILE_NAME, w);在下面的演示代码中我们以 w为例即文件不存在创建写入。以 w单纯打开文件c会自动清空文件内部的数据。
#include stdio.h
#include unistd.h// 我没有指明路径
#define FILE_NAME log.txtint mian()
{FILE* fp fopen(FILE_NAME, w); //r,w, r(读写,不存在出错),w(读写, 不存在创建), a(append, 追加), a()if (NULL fp){perror(fopen); //验证文件打开是否成功return 1;}int cnt 5;while (cnt){fprintf(fp, %s:%d\n, hello world, cnt--);}fclose(fp);}最后我们会把五个hello world写入到文件中 【补充】我们可以通过 cat 文件名指令打印对应的文件内容
2读取文件
fget读取文件以行为单位从特定的文件流中获取数据放到 s指向的缓冲区之中如果成功返回 s失败返回NUll。 在下面的演示代码中我们以 r操作为例即文件存在读取文件。
FILE* fp fopen(FILE_NAME, r); 代码如下示例
#include stdio.h
#include unistd.h
#include string.h// 我没有指明路径
#define FILE_NAME log.txtint mian()
{FILE* fp fopen(FILE_NAME, r); //r,w, r(读写,不存在出错),w(读写, 不存在创建), a(append, 追加), a()if (NULL fp){perror(fopen); //验证文件打开是否成功return 1;}char buffer[64];while(fgets(buffer, sizeof(buffer) - 1, fp) ! NULL)//#include string.h//fgets会把数组最后的\0换行也输入获取进去所以要-1{buffer[strlen(buffer) - 1] 0;//输入指令时需要按回车回车也会被也输入获取进去所以要将最后一位置0puts(buffer);}fclose(fp);}代码运行成功之后会输出文件内容 3追加文件内容
我们以 a操作为例即文件存在追加文件内容。
#include stdio.h
#include unistd.h
#include string.h// 我没有指明路径
#define FILE_NAME log.txtint mian()
{FILE* fp fopen(FILE_NAME, r); //r,w, r(读写,不存在出错),w(读写, 不存在创建), a(append, 追加), a()if (NULL fp){perror(fopen); //验证文件打开是否成功return 1;}int cnt 5;while (cnt){fprintf(fp, %s:%d\n, hello world, cnt--);}fclose(fp);}代码每成功运行一次会向文件中追加5个hello world的内容 【注意】其他语言会有其他的文件操作方法/接口以C为例会有 std::ifsteam::opean打开文件、std::ifsteam::ifsteam传文件流等有兴趣的小伙伴可以自己去了解一下哦~ 三、文本初始权限
文件创建的初始权限为666但是需要0666 ~umaskumask的默认值为0002因此我们普通文本类文件创建时大部分的权限都是664。 664的权限表示为 -rw-rw-r– 四、系统接口介绍
在上面的章节中我们学习的C语言进行文件操作的部分接口但这并不是我们本次学习最重要的知识下面通过对系统调用接口的学习相信大家一定能对文件的底层有更深入的了解。
1.open接口
opean打开或创建一个文件我们可以通过man指令来查看一下 open的基本信息
man 2 open #include sys/types.h
#include sys/stat.h
#include fcntl.hint open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);pathname: 要打开或创建的目标文件;flags: 打开文件时可以传入多个参数选项用下面的一个或者多个常量进行“或”运算构成flags;mode: 权限创建文件时设置文件的起始权限通常设置为 0666;参数:O_RDONLY: 只读打开O_WRONLY: 只写打开O_RDWR : 读写打开这三个常量必须指定一个且只能指定一个O_CREAT : 若文件不存在则创建它。需要使用mode选项来指明新文件的访问权限O_APPEND: 追加写O_TRUNC: 清空原文件返回值成功新打开的文件描述符descriptor失败-1———— 我是一条知识分割线 ————
2.open接口中的flag标记位
1flag标记位介绍 下面是文档中对 flag的描述 O_RDONLY - 只读O_WRONLY - 只写O_RDWR - 读写O_CREAT - 创建O_TRUNC - 清空文件
【注意】我们可以使用树划线 | 通道将不同的flag选项串联起来。
int fd open(FILE_NAME, O_WRONLY | O_CREAT | O_TRUNC, 0666);2flag底层原理仅作了解
在C语言中我们以一个整数int作为一个标记位。因为一个int由32个比特位构成我们让1存在于32位比特位中的不同的位置使用不同的比特位组合实现对不同情况的标识。open中的flag底层就是使用这样的原理进行选项传递的。 下面是一个应用比特位作为标识输出不同结果的代码示例 #include stdio.h
#include unistd.h
#include string.h// 每一个宏对应的数值只有一个比特位是1,彼此位置不重叠
#define ONE (10)
#define TWO (11)
#define THREE (12)
#define FOUR (13)void show(int flags)
{if(flags ONE) printf(one\n);if(flags TWO) printf(two\n);if(flags THREE) printf(three\n);if(flags FOUR) printf(four\n);
}int main()
{show(ONE);printf(-----------------------\n);show(TWO);printf(-----------------------\n);show(ONE | TWO);printf(-----------------------\n);show(ONE | TWO | THREE);printf(-----------------------\n);show(ONE | TWO | THREE | FOUR);printf(-----------------------\n);
}代码运行起来之后我们就可以根据不同的标记位输出不同的内容 ———— 我是一条知识分割线 ————
3.close
close - 系统级调用关闭文件
#include stdio.h
#include unistd.h
#include string.h
#include sys/types.h
#include sys/stat.h
#include fcntl.h
#include assert.h#define FILE_NAME log.txt int main()
{// 以只读的方式打开int fd open(FILE_NAME, O_RDONLY);if (fd 0){perror(open);return 1;}close(fd);
}———— 我是一条知识分割线 ————
4.write接口
通过 man查看 write的文档 参数fd - 想往哪个文件写buf - 对应文件的缓冲区在哪里count - 缓冲区对应的字节个数。 通过观察 write的接口我们可以发现有const void *类型参数的存在这也告诉了我们无论其他编程语言传入的是什么类型的数据文本、图片在系统调用接口操作系统的层面都可以将它们对应的二进制数据读取进去。
我们之前就提及过在C语言的文件操作接口中中以 w单纯代开文件c会自动清空文件内部的数据这是为什么呢我们的系统调用接口也会自动帮我们做吗实际上我们的系统调用接口并不会在下一次打开时自动清空里面的数据C语言的接口能完成是因为它做了相应的封装。
那我们要怎么样才能让系统调用接口在打开时就清空呢这里我们就需要在open中添加一些 flag选项了。
#include stdio.h
#include unistd.h
#include string.h
#include sys/types.h
#include sys/stat.h
#include fcntl.h
#include assert.h#define FILE_NAME log.txt int main()
{// 只写 新建//int fd open(FILE_NAME, O_WRONLY | O_CREAT, 0666);// 只写 新建 清除原文件内容int fd open(FILE_NAME, O_WRONLY | O_CREAT | O_TRUNC, 0666);if (fd 0){perror(open);return 1;}int cnt 5;char outBuffer[64];while (cnt){sprintf(outBuffer, %s:%d\n, aaaa, cnt--);// 你以\0作为字符串的结尾是C语言的规定和我文件有什么关系呢write(fd, outBuffer, strlen(outBuffer));}close(fd);
}———— 我是一条知识分割线 ————
5.read接口
通过 man查看 read的文档 参数fd - 读哪个文件buf - 对应文件的缓冲区在哪里count - 缓冲区对应的字节个数。 read返回值成功返回读到的字节数0则代表读到了文件的结尾。
假设我们的文件已经存在且其中保存的是字符串虽然系统调用接口理论上可以读取任何类型的文件但是我们还是需要根据实际情况为read提供读取结束的依据所以我们需要在下方增加一个字符串结束的判断。
int main()
{int fd open(FILE_NAME, O_RDONLY);if (fd 0){perror(open);return 1;}char buffer[1024];ssize_t num read(fd, buffer, sizeof(buffer) - 1);if (num 0) buffer[num] 0; // 0, \0, NULL - 0 为读取提供结尾printf(%s, buffer);close(fd);
}———— 我是一条知识分割线 ————
6.库函数接口和系统调用接口的关系
这里我们以C语言的库函数接口为例我们使用库函数接口实际上底层都调用了对应的系统调用接口。
也就是说库函数接口是系统调用接口的封装。对应的系统调用接口会将对应的返回值返回给库函数接口使其能完成对应的功能。其他编程语言以此类推 结语 基础IO - 文件操作(使用系统接口实现) 的知识大概就讲到这里啦博主后续会继续更新更多C 和 Linux 的相关知识干货满满如果觉得博主写的还不错的话希望各位小伙伴不要吝啬手中的三连哦你们的支持是博主坚持创作的动力