【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…...
Java如何权衡是使用无序的数组还是有序的数组
在 Java 中,选择有序数组还是无序数组取决于具体场景的性能需求与操作特点。以下是关键权衡因素及决策指南: ⚖️ 核心权衡维度 维度有序数组无序数组查询性能二分查找 O(log n) ✅线性扫描 O(n) ❌插入/删除需移位维护顺序 O(n) ❌直接操作尾部 O(1) ✅内存开销与无序数组相…...
centos 7 部署awstats 网站访问检测
一、基础环境准备(两种安装方式都要做) bash # 安装必要依赖 yum install -y httpd perl mod_perl perl-Time-HiRes perl-DateTime systemctl enable httpd # 设置 Apache 开机自启 systemctl start httpd # 启动 Apache二、安装 AWStats࿰…...
java调用dll出现unsatisfiedLinkError以及JNA和JNI的区别
UnsatisfiedLinkError 在对接硬件设备中,我们会遇到使用 java 调用 dll文件 的情况,此时大概率出现UnsatisfiedLinkError链接错误,原因可能有如下几种 类名错误包名错误方法名参数错误使用 JNI 协议调用,结果 dll 未实现 JNI 协…...
聊聊 Pulsar:Producer 源码解析
一、前言 Apache Pulsar 是一个企业级的开源分布式消息传递平台,以其高性能、可扩展性和存储计算分离架构在消息队列和流处理领域独树一帜。在 Pulsar 的核心架构中,Producer(生产者) 是连接客户端应用与消息队列的第一步。生产者…...
定时器任务——若依源码分析
分析util包下面的工具类schedule utils: ScheduleUtils 是若依中用于与 Quartz 框架交互的工具类,封装了定时任务的 创建、更新、暂停、删除等核心逻辑。 createScheduleJob createScheduleJob 用于将任务注册到 Quartz,先构建任务的 JobD…...
智能在线客服平台:数字化时代企业连接用户的 AI 中枢
随着互联网技术的飞速发展,消费者期望能够随时随地与企业进行交流。在线客服平台作为连接企业与客户的重要桥梁,不仅优化了客户体验,还提升了企业的服务效率和市场竞争力。本文将探讨在线客服平台的重要性、技术进展、实际应用,并…...
ESP32 I2S音频总线学习笔记(四): INMP441采集音频并实时播放
简介 前面两期文章我们介绍了I2S的读取和写入,一个是通过INMP441麦克风模块采集音频,一个是通过PCM5102A模块播放音频,那如果我们将两者结合起来,将麦克风采集到的音频通过PCM5102A播放,是不是就可以做一个扩音器了呢…...
python如何将word的doc另存为docx
将 DOCX 文件另存为 DOCX 格式(Python 实现) 在 Python 中,你可以使用 python-docx 库来操作 Word 文档。不过需要注意的是,.doc 是旧的 Word 格式,而 .docx 是新的基于 XML 的格式。python-docx 只能处理 .docx 格式…...
智能分布式爬虫的数据处理流水线优化:基于深度强化学习的数据质量控制
在数字化浪潮席卷全球的今天,数据已成为企业和研究机构的核心资产。智能分布式爬虫作为高效的数据采集工具,在大规模数据获取中发挥着关键作用。然而,传统的数据处理流水线在面对复杂多变的网络环境和海量异构数据时,常出现数据质…...
AGain DB和倍数增益的关系
我在设置一款索尼CMOS芯片时,Again增益0db变化为6DB,画面的变化只有2倍DN的增益,比如10变为20。 这与dB和线性增益的关系以及传感器处理流程有关。以下是具体原因分析: 1. dB与线性增益的换算关系 6dB对应的理论线性增益应为&…...


