【Linux初阶】基础IO - 文件操作(使用系统接口实现) | vim批量注释代码
🌟hello,各位读者大大们你们好呀🌟
🍭🍭系列专栏:【Linux初阶】
✒️✒️本篇内容:重新理解文件和文件操作,C语言实现的简单文件操作,文本初始权限,系统接口介绍(open、close、write、read)
🚢🚢作者简介:计算机海洋的新进船长一枚,请多多指教( •̀֊•́ ) ̖́-
文章目录
- 前言
- 一、重新谈论文件
- 二、重新谈论文件操作
- 1.除了C/C++,其他语言有文件接口吗?
- 2.vim知识补充 - 如何批量注释代码
- 3.C语言实现的简单文件操作
- (1)写入文件
- (2)读取文件
- (3)追加文件内容
- 三、文本初始权限
- 四、系统接口介绍
- 1.open接口
- 2.open接口中的flag标记位
- (1)flag标记位介绍
- (2)flag底层原理(仅作了解)
- 3.close
- 4.write接口
- 5.read接口
- 6.库函数接口和系统调用接口的关系
- 结语
前言
相信很多朋友在C语言学习阶段已经接触过文件操作的相关知识,在这篇文章中,我们会更深入的学习文件的知识,以操作系统的视角,重新认识和理解文件。
那么话不多数,让我们直接开始吧!
一、重新谈论文件
在学习文件之前,我们需要重新回顾一下文件的相关知识,让我们对文件有一个系统、立体的认识。
- 空文件,也要占据空间;
文件 = 内容 + 属性
;- 文件操作 = 对内容/属性 or 内容+属性的操作;
- 标定一个文件,必须使用:文件路径+文件名【唯一性】;
- 如果没有指明对应的文件路径,默认是在当前路径下进行文件访问;
- 当我们把 fopen,fclose,fread,fwrite等接口写好,代码编译完形成二进制可执行文件之后,如果没有运行起来,文件对应的操作不会被执行;
文件操作的本质:进程对文件的操作
; - 一个文件要被访问,就必须先被打开(用户进程调用接口,OS帮我们打开);
所以,我们研究文件操作,实际上就是在研究进程和被打开文件的关系
二、重新谈论文件操作
1.除了C/C++,其他语言有文件接口吗?
实际上,除了C/C++,Java、Python、php、go等语言都有自己的文件操作接口,它们的文件操作接口都不一样!那么问题来了,有那么多的文件操作接口,我们应该怎么降低我们的学习成本呢?
在解答上面这个问题之前,我们要明确我们的文件是如何被操作的。我们的文件储存在我们的磁盘中,磁盘 -> 硬件 -> OS(硬件被操作系统管理)->所有人想访问磁盘都不能绕过OS -> 使用OS提供的接口(文件级接口)。
所以无论上层语言怎么变化,a.库函数底层必须使用系统调用接口;b.库函数可以千变万化,但是底层不变。这就得出了我们降低学习成本问题的答案,只要我们学习不变的东西,我们就可以降低我们学习文件操作知识的成本了。在这篇文章中,我会着重讲述文件操作的底层原理和常见系统调用接口
。
———— 我是一条知识分割线 ————
2.vim知识补充 - 如何批量注释代码
在博主使用 vim进行学习的过程中,常常需要对其中的代码进行大范围操作,经过网络搜索发现各种阅读量靠前的信息并不能满足自己对 vim操作快速便捷简明的要求,因此在此处根据自己的编码习惯对 vim相应的知识做相应的补充。
批量化注释代码:Ctrl+v
进入块选择模式(V-BLOCK模式),h、j、k、l
分别代表左下上右
,控制块的大小,输入大写 I
,此时下方会提示进入“insert”模式,再输入注释符//
,按Esc回到初始NORMAL模式
(返回后才会完成批量化注释)。
撤销上一次的操作:初始NORMAL模式下按 u。
取消批量注释:Ctrl+v进入块选择模式(V-BLOCK模式),选中你要删除的行首的注释符号,注意// 要选中两个,输入d 即可完成取消批量注释。
调整代码格式 + 批量删除代码:Ctrl+v进入块选择模式(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.txt"int 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.txt"int 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.txt"int 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 & ~umask,umask的默认值为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.h>int 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标记位
(1)flag标记位介绍
下面是文档中对 flag的描述
- O_RDONLY - 只读
- O_WRONLY - 只写
- O_RDWR - 读写
- O_CREAT - 创建
- O_TRUNC - 清空文件
【注意】我们可以使用树划线 | (通道),将不同的flag选项串联起来
。
int fd = open(FILE_NAME, O_WRONLY | O_CREAT | O_TRUNC, 0666);
(2)flag底层原理(仅作了解)
在C语言中,我们以一个整数(int)作为一个标记位。因为一个int由32个比特位构成,我们让1存在于32位比特位中的不同的位置,使用不同的比特位组合,实现对不同情况的标识。open中的flag底层就是使用这样的原理进行选项传递的。
下面是一个应用比特位作为标识,输出不同结果的代码示例
#include <stdio.h>
#include <unistd.h>
#include <string.h>// 每一个宏,对应的数值,只有一个比特位是1,彼此位置不重叠
#define ONE (1<<0)
#define TWO (1<<1)
#define THREE (1<<2)
#define FOUR (1<<3)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 的相关知识,干货满满,如果觉得博主写的还不错的话,希望各位小伙伴不要吝啬手中的三连哦!你们的支持是博主坚持创作的动力!💪💪
相关文章:

【Linux初阶】基础IO - 文件操作(使用系统接口实现) | vim批量注释代码
🌟hello,各位读者大大们你们好呀🌟 🍭🍭系列专栏:【Linux初阶】 ✒️✒️本篇内容:重新理解文件和文件操作,C语言实现的简单文件操作,文本初始权限,系统接口介…...
网络安全之信息收集
第一部分:被动信息收集 1、简介 在信息收集这块区域,我将其分为两部分:第一部分即被动信息收集,第二部分即主动信息收集。 对于标准的渗透测试人员来说,当明确目标做好规划之后首先应当进行的便是信息收…...

ModuleNotFoundError: No module named ‘_lzma‘
安装torchvision报错:ModuleNotFoundError: No module named ‘_lzma’ 参考文章:https://zhuanlan.zhihu.com/p/404162713 解决思路:用backports.lzma代替_lzma包 解决步骤:(ubuntu系统) 安装依赖sudo apt-get install liblzma-d…...
标点符号相关的英语单词
Comma - 逗号 Period - 句号 Question mark - 问号 Exclamation mark - 感叹号 Semicolon - 分号 Colon - 冒号 Quotation marks - 引号 Parentheses - 括号 Brackets - 方括号 Hyphen - 连字符 Dash - 破折号 Ellipsis - 省略号 Apostrophe - 省略符号 Slash - 斜杠 Backslash…...
MyBatis的部分知识点
一、resultMap的constructor配置方式 <resultMap id"" type""> <constructor> <!--主键--> <idArg column"id" javaType"_int"/> <!--其他列--> …...
PAT A1089 Insert or Merge
1089 Insert or Merge 分数 25 作者 CHEN, Yue 单位 浙江大学 According to Wikipedia: Insertion sort iterates, consuming one input element each repetition, and growing a sorted output list. Each iteration, insertion sort removes one element from the input…...

研发工程师玩转Kubernetes——创建一个测试容器
测试容器并不是什么都没有的容器,只是它没有我们期望的常驻进程。我们常用它来做一些测试。 举个例子,在《研发工程师玩转Kubernetes——自动扩缩容》中我们使用本地wrk进行了压力测试。如果我们希望进入容器手工调用wrk,该怎么做呢ÿ…...

FPGA - 7系列 FPGA内部结构之CLB -03- CLB相关原语以及应用
前言 本文节选UG474的第二章,进行整理翻译。CLB资源被FPGA综合工具自动有效地使用,不需要任何特殊的FPGA专用编码。一些HDL编码建议和技术可以帮助优化设计以获得最大效率。 设计检查清单 这些指南是为有效使用7系列CLB的设计建议提供的快速核对表。7…...

什么是日志关联
什么是日志关联 日志关联是一种分析来自不同源的日志数据以识别事件模式的技术。它用于更好地了解网络的活动,从而有效地保护网络免受漏洞和威胁。 日志关联是日志管理过程的关键部分。收集和存储日志后,集中式日志服务器将执行分析以检测特定事件。日…...

打家劫舍问题 Python题解
✅作者简介:人工智能专业本科在读,喜欢计算机与编程,写博客记录自己的学习历程。 🍎个人主页:小嗷犬的个人主页 🍊个人网站:小嗷犬的技术小站 🥭个人信条:为天地立心&…...

【JavaSE】Java基础语法(十八):接口
文章目录 1. 接口的概述2. 接口的特点3. 接口的成员特点4. 类和接口的关系5. 抽象类和接口的关系 1. 接口的概述 接口就是一种公共的规范标准,只要符合规范标准,大家都可以通用。Java中接口存在的两个意义 用来定义规范用来做功能的拓展 2. 接口的特点…...
SVD求解两组多维点之间的欧式变换矩阵,及halcon代码实现
之前研究了二维点的仿射变换,用解矩阵的方式求解了两组二维点之间的变换矩阵。 学习了下SVD,看到可以用SVD求解两组多维点之间的欧式变换矩阵,当然也是个最优化问题。 这里的变换只有平移和旋转,没有缩放。 一、先说结论&#…...

常用监控方案 Prometheus + Grafana 简单使用小结
文章目录 前言一、概念1.1 发展1.2 时序数据1.3 Metric 二、Prometheus2.1 架构2.2 配置2.3 查询语言PromQL2.4 Exporter 三、Grafana3.1 数据源3.2 权限3.3 面板可视化3.4 仪表盘 四、实战4.1 监控 Windows/Linux4.2 监控 JVM4.3 监控 MySQL4.4 监控 Springboot API 参考 前言…...
基于长短期神经网络LSTM的飞行轨迹跟踪预测,基于长短期神经网络LSTM的三维路径预测
目录 背影 摘要 LSTM的基本定义 LSTM实现的步骤 基于长短期神经网络LSTM的飞行轨迹跟踪 完整代码: https://download.csdn.net/download/abc991835105/87705046 效果图 结果分析 展望 参考论文 背影 路径追踪预测,对实现自动飞行驾驶拥有重要意义,长短期神经网络是一种改进…...

计算机组成原理-指令系统-指令格式及寻址方式
目录 一、指令的定义 1.1 扩展操作码指令格式 二、指令寻址方式 2.1 顺序寻址 2.2 跳跃寻址 三、 数据寻址 3.1 直接寻址 3.2 间接寻址 3.3 寄存器寻址 3.4 寄存器间接寻址 3.5 隐含寻址 3.6 立即寻址 3.7 偏移地址 3.7.1 基址寻址 3.7.2 变址寻址 3.7.3 相对寻址…...

【满分】【华为OD机试真题2023B卷 JAVAJS】经典屏保
华为OD2023(B卷)机试题库全覆盖,刷题指南点这里 经典屏保 知识点循环迭代编程基础 时间限制:1s 空间限制:256MB 限定语言:不限 题目描述: DVD机在视频输出时,为了保护电视显像管,在待机状态会显示“屏保动画”,如下图所示,DVD Logo在屏幕内来回运动,碰到边缘会反弹…...

Apache 网页与安全优化
目录 一:Apache网页优化概述 1、概述 2、优化内容 3、gzip介绍 4、Apache的压缩模块 5、mod_ gzip 模块与mod_ deflate 模块 二: 网页压缩 1.检查是否安装 mod_deflate 模块 2. 如果没有安装mod_deflate 模块,重新编译安装 Apache 添…...
Unity的IFilterBuildAssemblies:深入解析与实用案例
Unity IFilterBuildAssemblies Unity IFilterBuildAssemblies是Unity引擎中的一个非常有用的功能,它可以让开发者在构建项目时自定义哪些程序集需要被包含在构建中,哪些程序集需要被排除在建之外。这个功能可以帮助开发者更好地控制项目的构建过程&…...

分片架构,Redis Cluster 分析
分片架构解决的问题 通过堆机器,提升读写性能,与存储性能 分片架构设计要点 分片规则 选择Cardinality大的作为分片键,尽可能保证数据分布均匀 常见分片键: 基于主键(业务型数据),基于时间…...
Linux-0.11 文件系统bitmap.c详解
Linux-0.11 文件系统bitmap.c详解 模块简介 该模块包含了两对函数,第一对是和i节点相关的free_inode()和new_inode()。第二对是和逻辑块相关的free_block()和new_block()。 函数详解 free_block void free_block(int dev, int block)该函数的作用是释放设备dev…...

Unity3D中Gfx.WaitForPresent优化方案
前言 在Unity中,Gfx.WaitForPresent占用CPU过高通常表示主线程在等待GPU完成渲染(即CPU被阻塞),这表明存在GPU瓶颈或垂直同步/帧率设置问题。以下是系统的优化方案: 对惹,这里有一个游戏开发交流小组&…...

2021-03-15 iview一些问题
1.iview 在使用tree组件时,发现没有set类的方法,只有get,那么要改变tree值,只能遍历treeData,递归修改treeData的checked,发现无法更改,原因在于check模式下,子元素的勾选状态跟父节…...

如何将联系人从 iPhone 转移到 Android
从 iPhone 换到 Android 手机时,你可能需要保留重要的数据,例如通讯录。好在,将通讯录从 iPhone 转移到 Android 手机非常简单,你可以从本文中学习 6 种可靠的方法,确保随时保持连接,不错过任何信息。 第 1…...

ardupilot 开发环境eclipse 中import 缺少C++
目录 文章目录 目录摘要1.修复过程摘要 本节主要解决ardupilot 开发环境eclipse 中import 缺少C++,无法导入ardupilot代码,会引起查看不方便的问题。如下图所示 1.修复过程 0.安装ubuntu 软件中自带的eclipse 1.打开eclipse—Help—install new software 2.在 Work with中…...
数据库分批入库
今天在工作中,遇到一个问题,就是分批查询的时候,由于批次过大导致出现了一些问题,一下是问题描述和解决方案: 示例: // 假设已有数据列表 dataList 和 PreparedStatement pstmt int batchSize 1000; // …...

安宝特方案丨船舶智造的“AR+AI+作业标准化管理解决方案”(装配)
船舶制造装配管理现状:装配工作依赖人工经验,装配工人凭借长期实践积累的操作技巧完成零部件组装。企业通常制定了装配作业指导书,但在实际执行中,工人对指导书的理解和遵循程度参差不齐。 船舶装配过程中的挑战与需求 挑战 (1…...
C#学习第29天:表达式树(Expression Trees)
目录 什么是表达式树? 核心概念 1.表达式树的构建 2. 表达式树与Lambda表达式 3.解析和访问表达式树 4.动态条件查询 表达式树的优势 1.动态构建查询 2.LINQ 提供程序支持: 3.性能优化 4.元数据处理 5.代码转换和重写 适用场景 代码复杂性…...
MinIO Docker 部署:仅开放一个端口
MinIO Docker 部署:仅开放一个端口 在实际的服务器部署中,出于安全和管理的考虑,我们可能只能开放一个端口。MinIO 是一个高性能的对象存储服务,支持 Docker 部署,但默认情况下它需要两个端口:一个是 API 端口(用于存储和访问数据),另一个是控制台端口(用于管理界面…...
python爬虫——气象数据爬取
一、导入库与全局配置 python 运行 import json import datetime import time import requests from sqlalchemy import create_engine import csv import pandas as pd作用: 引入数据解析、网络请求、时间处理、数据库操作等所需库。requests:发送 …...
十九、【用户管理与权限 - 篇一】后端基础:用户列表与角色模型的初步构建
【用户管理与权限 - 篇一】后端基础:用户列表与角色模型的初步构建 前言准备工作第一部分:回顾 Django 内置的 `User` 模型第二部分:设计并创建 `Role` 和 `UserProfile` 模型第三部分:创建 Serializers第四部分:创建 ViewSets第五部分:注册 API 路由第六部分:后端初步测…...