【Linux初阶】基础IO - 文件操作(使用系统接口实现) | vim批量注释代码

news2024/11/25 22:53:57

🌟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语言学习阶段已经接触过文件操作的相关知识,在这篇文章中,我们会更深入的学习文件的知识,以操作系统的视角,重新认识和理解文件。

那么话不多数,让我们直接开始吧!


一、重新谈论文件

在学习文件之前,我们需要重新回顾一下文件的相关知识,让我们对文件有一个系统、立体的认识。

  1. 空文件,也要占据空间;
  2. 文件 = 内容 + 属性
  3. 文件操作 = 对内容/属性 or 内容+属性的操作;
  4. 标定一个文件,必须使用:文件路径+文件名【唯一性】;
  5. 如果没有指明对应的文件路径,默认是在当前路径下进行文件访问;
  6. 当我们把 fopen,fclose,fread,fwrite等接口写好,代码编译完形成二进制可执行文件之后,如果没有运行起来,文件对应的操作不会被执行;文件操作的本质:进程对文件的操作
  7. 一个文件要被访问,就必须先被打开(用户进程调用接口,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;//输入指令时需要按回车,回车也会被也输入获取进去,所以要将最后一位置0
        puts(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 的相关知识,干货满满,如果觉得博主写的还不错的话,希望各位小伙伴不要吝啬手中的三连哦!你们的支持是博主坚持创作的动力!💪💪

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/587633.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【spring源码系列-01】spring底层源码整体概述

JVM系列整体栏目 内容链接地址【一】spring源码整体概述https://blog.csdn.net/zhenghuishengq/article/details/130940885 初识虚拟机与java虚拟机 一&#xff0c;spring源码整体概述1&#xff0c;初步概述2&#xff0c;扩展点机制3&#xff0c;核心方法refresh4&#xff0c;B…

【wpf】xaml 中的参数复用

背景 xaml中有几种复用的方式&#xff1a; 有时在xaml中&#xff0c;我们需要复用一些参数&#xff0c;比如 固定的一个值。 有时是固定的一个样式。 资源&#xff0c;sys的引入 有时多个控件都要设置一个高度&#xff0c;我可以引入sys 声明 我就使用这个吧&#xff1a…

扬帆出海正当时,企业应该做好哪些准备?

在跨境出海的时代大潮中&#xff0c;想要拓展海外市场的中国企业&#xff0c;应该事先做好哪些准备&#xff1f; 中国企业出海的新格局 首先来看一组令人振奋的数据。来自中国信通院的数据显示&#xff0c;在2020年的时候&#xff0c;中国数字经济的规模就达到了39.2万亿元人民…

本地Linux搭建web服务并发布公网访问

文章目录 前言1. 本地环境服务搭建2. 局域网测试访问3. 内网穿透3.1 ubuntu本地安装cpolar内网穿透3.2 创建隧道3.3 测试公网访问 4. 配置固定二级子域名4.1 保留一个二级子域名4.2 配置二级子域名4.3 测试访问公网固定二级子域名 转载自cpolar极点云的文章&#xff1a;在Ubunt…

玩转华为云Astro低代码体验季

目录 Astro轻应用应用场景 零代码应用构建 轻应用构建 行业应用构建 业务大屏构建 使用体验 功能建议 总体评价 Astro轻应用&#xff08;Astro Zero&#xff0c;简称AstroZero&#xff09;是华为云为行业客户、合作伙伴、开发者量身打造的低代码/零代码应用开发平台&#xff0c…

C++ A lambda function

lambda 函数是 C 中的匿名函数&#xff0c;可以内联定义并用作函数对象。 下面是定义 lambda 函数的一般语法&#xff1a; [capture list] (parameter list) -> return type { function body }lambda 语法的每个部分&#xff1a; - capture list&#xff1a;这是一个可选的…

R实践——【rgplates】功能函数解析

【rgplates】功能函数解析 1. 板块和特征重建2. 构造模型表示2.1 用法2.2 参数2.3 值2.4 示例 3. 实用工具3.1 用法3.2 参数3.3. 值3.4 示例 1. 板块和特征重建 一切与几何形状重建到过去状态有关的东西 reconstruct()&#xff1a;重建地理特征 详见R语言实践——古今地理坐…

4. WebGPU 存储缓冲区 (WebGPU Storage Buffers)

这篇文章是关于存储缓冲区的&#xff0c;我们从上一篇文章暂停的地方继续。 存储缓冲区在许多方面类似于统一缓冲区。如果我们所做的只是将 JavaScript 中的 UNIFORM 更改为 STORAGE 并将 WGSL 中的 var 更改为 var<storage, read> &#xff0c;那么上一页中的示例就可以…

Zabbix“专家坐诊”第193期问答汇总

问题一 Q&#xff1a;大佬们&#xff0c;怎么才能将zabbix-server接收到的数据全部展示出来呢&#xff1f;目前我的显示数据无法全部显示。 A&#xff1a;这个是用zabbix_sender发送过来的&#xff1f;确认下数据中是否包含空格等&#xff0c;如果有空格使用反斜杠转义或者单…

uniapp内置组件

目录 3.1、视图容器 view scroll-view swiper match-media 3.2、表单组件 form input App平台iOS端软键盘上方横条去除方案 关于软键盘弹出的逻辑说明 关于软键盘收起的逻辑说明 picker 3.3、 路由与页面跳转 navigator组件 3.4、 地图 map 3.1、视图容器 所有…

2.5. 重载与覆盖

在 Java 中&#xff0c;方法的重载&#xff08;Overloading&#xff09;和覆盖&#xff08;Overriding&#xff09;是两个重要的概念。它们都涉及到方法的定义与使用&#xff0c;但作用和规则有所不同。 重载&#xff08;Overloading&#xff09; 重载是指在同一个类中定义多…

2023年6月18日DAMA-CDGA/CDGP数据治理认证报名到这里

DAMA认证为数据管理专业人士提供职业目标晋升规划&#xff0c;彰显了职业发展里程碑及发展阶梯定义&#xff0c;帮助数据管理从业人士获得企业数字化转型战略下的必备职业能力&#xff0c;促进开展工作实践应用及实际问题解决&#xff0c;形成企业所需的新数字经济下的核心职业…

在家远程使用公司用友ERP财务软件 【远程办公】

文章目录 前言1.本地访问简介2. cpolar内网穿透3. 公网远程访问4. 固定公网地址 转发自cpolar极点云的文章&#xff1a;外网远程访问公司内网用友畅捷通T财务软件 – 远程办公 前言 用友畅捷通T适用于异地多组织、多机构对企业财务汇总的管理需求&#xff1b;全面支持企业对远…

webpack简单的搭建和使用(1)

随便创建一个空的文件夹&#xff0c;例如说&#xff1a;explore 然后我们测试一下我们的node是否存在 可以正确打印出版本 我们再次输入&#xff1a;npm init -y 创建一个package.json文件 出现这样的情况就成功了 然后我们要安装webpack在终端上输入命令&#xff1a; npm i …

数据结构图的基础概念

1、图的概念 图(Graph)&#xff1a;是由顶点的有穷非空集合和顶点之间边的集合组成。顶点(Vertex)&#xff1a;图中的数据元素。边(Edge)&#xff1a;顶点之间的逻辑关系,边可以是有向的或无向的&#xff0c;也可以带有权重&#xff08;可以表示距离&#xff0c;花费等&#xf…

GaussDB云数据库SQL应用系列-视图管理

一、前言 GaussDB是一款基于云计算技术的高性能关系型数据库&#xff0c;支持多种数据模型和分布式架构。在GaussDB中&#xff0c;视图管理是非常重要的一项功能&#xff0c;它可以帮助用户更方便地管理和查询数据。 数据库视图管理是指对数据库中的视图进行创建、修改、删除…

美债危机现曙光,比特币再破2万8

* * * 原创&#xff1a;刘教链 * * * 号外&#xff1a;今天在小号“刘教链Pro”发表了一篇《常常自律&#xff0c;偶尔放纵》&#xff0c;谈了一下关于意志力和自制力的非同寻常的科学研究结论&#xff0c;及其对投资方法的启迪&#xff0c;欢迎关注“刘教链Pro”并阅读。 * *…

AI专业教您保姆级在暗影精灵8Windows11上本地部署实现AI绘画:Stable Diffusion(万字教程,多图预警)

目录 一、Stable Diffusion介绍 二、Stable Diffusion环境搭建 1.Anaconda下载与安装 2.Pycharm&#xff08;IDE&#xff09;下载与安装 3.CUDA、CuDNN下载与安装 三、Stable Diffusion的本地部署 1.克隆项目到本地 2.初始化打开项目 3.安装环境所需库 4.运行代码…

《逆袭进大厂》之C++篇49问49答

它是在 github 上的 clone 下来的仓库笔记 自己看书理解到的知识点 网上相关问题的博客总结这几大基础上慢慢总结形成的&#xff0c;并不仅仅只是简单的收集整理&#xff0c;没有加入自己思考的笔记没有灵魂。 在接下来的十篇文章里我会陆陆续续将自己的秋招笔记整理出来&…

家政服务预约APP的系统设计与实现

摘 要&#xff1a;针对家政行业蓬勃发展&#xff0c;老套的家政服务方式已经跟不上互联网时代的步伐这个问题。基于Android移动平台的分析和设计过程、C/S模式、Eclipse平台&#xff0c;采用Java语言进行开发设计&#xff0c;设计了基于MVC架构的实现方案。安卓客户端与服务器…