Linux之重谈文件和c语言文件接口

重谈文件

文件 = 内容 + 属性, 所有对文件的操作都是: a.对内容操作 b.对属性操作

关于文件 

一:
即使文件的内容为空,该文件也会在磁盘上也会占空间,因为文件不仅仅只有内容还有文件对应的属性,文件的内容会占用空间, 文件的属性也会占用空间,比如:

创建了一个文件没有往文件里面写入任何内容, 但文件的大小为0代表文件内容为空, 不代表文件本身不占用空间, 因为文件 = 内容 + 属性, 内容是数据, 属性也是数据 ---存储文件必须既存储内容又存储属性----默认就是磁盘上的文件.

对文件的操作等于对文件的属性进行操作或者对文件的属性和内容进行操作, 因为改变文件的属性不一定会改变文件的内容, 但是改变文件的内容一定会改变文件的属性, 比如:

修改之前的test.c文件: 

对test.c文件的内容进行修改, 多输入一行printf语句: 

对文件的内容做修改, 文件的内容和属性都发生变化. 

只对文件的属性进行操作, 比如说改变文件的拥有者, 所属组, 文件的权限和时间等等都是对文件的属性进行的操作:

进行文件操作时需要使用路径加文件名的方式以确保唯一性, 因为在不同的路径下可能会存在同名文件, 比如在上级目录下创建一个空文件test.c, 然后在其他路径下使用另外一个可执行程序来向test.c文件写入内容:

路径加文件名可以帮我们指明确定的文件.

四:
如果没有指名对应的文件路径, 默认是在当前路径进行访问, 这里的当前路径就是进程当前的路径,就是在哪个路径下执行的文件, 那么这个路径就是进程的当前路径。

 test.c:

 之前说过在根目录下proc文件夹里面存放许多文件夹, 这些文件夹以系统中的各种进程名为名, 并装着对应进程的各种信息:

此时就会显示两个链接文件, 链接文件exe表示的是该进程对应的文件在磁盘中的位置,链接文件cwd表示的是该进程的当前路径:

如果我们在其它路径下执行的这个程序,, 那么该程序的cwd就会变化,比如在家目录:

五:

当我们把fopen, fclose, fwrite等接口写完之后, 代码编译之后, 形成二进制可执行程序之后如果程序被没有执行, 则对应的文件操作是不会被执行的, 所以对文件的操作本质上就是进程对文件的操作.
我们访问一个文件时, 都是要先把这个文件先打开的, 这里的"我们" 其实是进程, 是进程要访问这个文件. 而文件打开前是一个普通的磁盘文件, 而CPU只与内存打交道, 所以文件打开后其实把文件加载到了内存, 加载磁盘上的文件, 一定涉及到访问硬件磁盘设备, 所以文件打开操作是由操作系统来做的.

六:

磁盘上有很多的文件, 但是并不是所有的文件都被打开了, 文件按照是否被打开, 分为: 被打开的文件(内存中) 和 未被打开的文件(磁盘中). 所以研究文件操作的本质就是进程与被打开文件的关系.

一个进程是可以打开多个文件的, 也就是说加载到内存中的文件可能存在多个, 操作系统要不要管理这些打开的文件呢? 如何管理? 先描述,再组织, 一个文件要被打开, 一定要在内核中形成被打开的文件对象, 所以对文件的管理转化为对链表的增删查改.

struct XXX
{//文件属性struct XXX* next;
};

 C语言文件调用函数

在谈系统调用接口之前先来谈谈c语言里面的文件调用函数.

fopen

操作文件之前首先需要打开文件, 在c语言里面打开文件使用fopen函数:

第一个参数表示要打开文件的文件名,  如果文件名没有带路径的话, 该函数就会在当前路径下查找并打开这个文件, 如果带了路径就会到指定路径下查找并打开这个文件.

第二个参数表示的是打开文件的方式, r表示只读也就是只能从文件中读取数据, w表示只写也就是只能往文件中写入数据, a表示的是往文件的后面尾插数据, 以w的形式打开文件如果文件名不存在的话会在当前路径下创建文件, 并且w方式打开文件会清空文件中原来的数据, 比如说下面的操作:

而之前在指令中使用过的输出重定向 > 追加重定向 >>, 实际上是分别以w方式和a方式打开文件: 

文件以w方式打开, 会先清空文件内容, 而使用 echo "hello world" > log.txt 是以输出重定向的方式向log.txt中写入hello world, 每次打开文件都会先清空, 所以如果直接 > log.txt , 可以直接清空该文件, 虽然什么内容都没有向文件内输入, 但是重定向会以"w"的方式打开文件, 打开文件就会清空文件.

文件以a方式打开, 是从文件结尾处开始写入, 是追加. 使用echo "hello world" >> log.txt, 以追加重定向的方式向log.txt中写入hello world, 每次写入之前的内容都不会被清除, 所以 >> 追加重定向是以"a"的方式打开文件.


fclose

既然有函数能够打开文件那么就会有函数关闭文件, 那这里的函数就是fclose函数, 该函数的介绍如下:

这个函数只有一个参数, 所以使用这个函数的时候直接将文件打开时创建的那个FILE*变量传给这个函数就可以关闭对应文件了 .


fwrite

size_t fwrite(const void* buffer,size_t size,size_t count,FILEstream);

从内存的变量中获取二进制数据,放到文件中
const void buffer表示获取数据的位置,size_t size一个变量类型的大小,size_t count表示读多少个这样类型的数据,FILE* stream为文件指针

fread

size_t fread( void buffer, size_t size, size_t count, FILE stream );

从文件中获取二进制数据, 放到内存的变量中

const void buffer表示获取数据的位置,size_t size一个变量类型的大小,size_t count表示读多少个这样类型的数据,FILE stream为文件指针


 


认识系统接口

c/c++都有文件操作接口, 每个语言都有文件操作接口, 并且每个语言操作接口都还不一样, 但是这些接口本质上都是调用操作系统提供的文件级别的系统调用接口来访问的文件, 而操作系统的接口只有一套, 不管库函数再怎么变化. 底层是不变的.

文件是在硬盘上的, 硬盘是外设, 外设是被操作系统管理的, 所有人想要访问磁盘都无法绕操作系统,所以访问文件一定要有系统调用接口, C语言打开文件的接口, 底层一定封装了系统调用接口.

标记位

宏喜欢用来作为标记位, 标记位的作用就是表明某件事情是否发生/存在, 如果发生了就传一个标记位, 在c语言中一般以一个整型变量作为标记位, 但是如果需要10个标记位的话那就得传10个变量用来表示10件事情已经发生这就有点麻烦, 所以结合之前学过的位图去判断是否存在的问题就十分合适了,用一个整形变量可以充当32个标记位.

首先标记位是一个宏, 这个宏实际上就是一个数字, 并且其对应的二进制位只有一位是1, 所以我们就可以采用这样的形式来创建标记位:

  1 #include <stdio.h>2 3 #define Print1 (1<<0) //00014 #define Print2 (1<<1) //00105 #define Print3 (1<<2) //01006 #define Print4 (1<<3) //10007 8 void Print(int flags)9 {10     if(flags & Print1) printf("hello 1\n");11     if(flags & Print2) printf("hello 2\n");12     if(flags & Print3) printf("hello 3\n");13     if(flags & Print4) printf("hello 4\n");                                                                                                                                                                   14 }15 16 int main()17 {18     Print(Print1); //hello 119     Print(Print1 | Print2); //hello 1\n hello 220     Print(Print1 | Print2 | Print4); //hello 1\n hello 2\n hello 421     return 0;22 }

利用位图的原理, 想要执行哪条代码就输入对应的宏并用或运算符连接起来, 因为每一个宏都对应唯一的一个二进制1. 


open 

c语言里面是通过函数fopen来以各种不同的形式打开文件, 其实fopen是通过对系统调用接口open进行封装来实现的, open函数的介绍:

open函数有三个参数, 第一个参数pathname是文件路径, 第二个参数flags是打开方式也就是标志位, 第三个参数是权限, 如果文件已经存在, 只需要前两个参数. 文件不存在需要设置第三个参数, 否则创建出的文件没有权限.

部分标识符: 

选项宏

功能

O_RDONLY

只读打开

O_WRONLY

只写打开

O_RDWR

读,写打开

O_CREAT

若文件不存在,则创建它。需要使用mode选项,来指明新文件的访问权限

O_TRUNC当源文件中存在内容时会将文件的内容进行清空

O_APPEND

追加写

注:O_RDONLY、O_WRONLY和O_RDWR只能三选一使用 .

其中第一个参数pathname可以添加路径也可以不添加路径, 不添加路径的话该函数就会在当前路径下查找文件. flags可以传很多的标志位, 先举两个例子, O_RDONLY表示只读标记符, O_WRONLY表示只写标记符, 和fopen里的 r 和 w 有点像.

我们可以通过下面的例子来证明一下两者的区别:

首先c语言中的w有两个特性:

1.如果源文件存在内容的话那么以只写方式打开文件的话, 会将源文件的内容清空。

2.如果打开的文件不存在的话, 那么fopen函数会自己创建一个文件.

我们来看看open函数的只写有没有上述的功能比如说下面的操作, 在当前路径下创建一个文件:

并没有自动创建文件

所以再介绍open函数的两个标记位: O_CREATO_TRUNC, 其中O_CREAT的作用就是当文件不存在时会自动创建一个文件, O_TRUNC的作用就是当源文件中存在内容时会将文件的内容进行清空(create是创建, trunc是截断), 所以使用c语言的fopen函数以w只读的形式打开文件时, 在底层就会调用open函数并以O_CREAT | O_TRUNC|O_WRONLY 作为标记位进行传参:

发现可以自动创建一个log.txt文件, 但是这个文件目前是红色的, 因为此时的文件里面都是乱码无法正常的使用, 造成这种现象的原因是因为在创建文件的时候没有给对应的权限, 也就是上面提到的遗漏了该函数的第三个参数, 当使用open函数创建文件时需要用第三个参数给创建的文件一个起始权限.

这里给了起始权限是0666, 但是这里创建出来的权限是0664, 因为这里创建的文件也遵循umask的原则, 所以创建出的文件的权限可能不是我们想要的.

这里要是想让创建的文件就是我们给的起始权限的话就可以使用umask函数, 可以在创建文件前先用umask(0), 先把权限掩码设置为0, 设置了权限掩码就会用设置的, 没设置umask就是系统默认的.但是程序中调用的umask函数不会影响命令行的umask值.

 

open函数返回值称为: 文件描述符fd.

使用open函数打开文件时open函数会返回一个整数, 这个整数就是文件描述符fd, 在其他函数里面就可以根据这个文件描述符来确定要写入的文件, 如果open返回的值为负数的话就表明此时文件打开失败.

我们可以看到此时打印的文件描述符为3,在后面的程序就可以使用这个文件描述符3来代表要被操作的文件mytest。


write

将一个文件以写的打开之后就可以往这个文件里面写入内容, 那么这里的写入就要用到write函数, 该函数的参数如下:

第一个参数fd是文件描述符表示要将内容写入哪个文件.

第二个参数表示写入文件的内容来自于哪个缓冲区, 这个指针会指向空间开始的位置, 并且指针的类型为void说明不管要写入的数据类型是什么这个函数都可以将数据写到文件里面, 就是因为在计算机看来所有的数据全部都是二进制, 我们平时说的二进制数据和文本数据只不过语言进行的封装罢了.

第三个参数表示写入文件的内容有多少个字节, 当这个函数执行完之后就会返回实际写入文件的字节个数.

比如说下面的代码: 

问题: write函数第三个参数需不需要传strlen(buf)+1 ?

outbuffer中有一段字符串abcde\n, 这个字符串的长度为6大小为6个字节而我们却想往文本里面写入7个字节的内容, 所以当函数写入的时候就会自动的在字符串后面添加上一个\0来补齐这7个字节的大小, 我们之所以这么认为是因为在c语言当中字符串是以\0作为字符串结束的标志符, 防止出现一些越界访问的错误, 可是字符串以\0结尾是c语言规定的和文件有关系吗?

没关系, 文件中的字符串不是以\0结尾的往文件中写入的时候只需要字符串的有效内容就可以, 除非就想往文件中写入\0不然不要在这里的第三个参数+1.

再介绍一个标志位O_APPEND, 如果想要在文件的尾部插入内容, 达到fopen中"a"的效果, 就要将open函数第二个参数O_TRUNC替换为O_APPEND.

在刚才的基础上追加了一遍原来的内容.

所以标记位 O_WRONLY|O_APPEND|O_CREAT 组合到一起就是c语言中fopen中a的功能


read

既然可以将缓冲区(数组)里面的内容通过write函数输出到文件里面, 那么这里也可以通过read函数读取文件里面的内容并放到缓冲区里面, read函数的参数如下:

与write函数相似,

第一个参数fd表示要读取哪个文件的内容.

第二个参数buf表示将读取的内容放入程序的哪个缓冲区中.

第三个参数count表示你要读取多少个字节的内容.

read函数的返回值表示如果读取成功了就返回读取的字符个数, 如果读取失败了或者没有内容就返回0.

read函数的第二个参数类型是void*, 表明read函数在读取内容的时候也没有数据类型的概念,不管文件里面装的是图片还是视频还是一些文本数据等等, 它读的都是二进制数据, 这些数据具体如何处理那都是使用者自己决定的.

比如:

使用read函数读取文本中的数据, 因为这里采用的是系统提供的函数读取数据, 并且我们想让数据以字符串的形式放入到数组里面, 所以read函数里面的读取字符的个数得是sizeof(buffer)-1留下来一个空间以免缓冲区满了装不下\0, 读取完数据之后就要在缓冲区的有效内容的后面手动添加一个\0用来表示此时的数据内容是字符串.

把刚才写入的内容再读出来并打印. 


close

close函数就是用来关闭open函数打开的文件的, 这个函数的参数如下:

将文件描述符传给close函数就可以.

所以其实fopen fclose fwrite fread fseek等函数在底层其实都是用操作系统提供的接口实现的, 这些函数与open close write read lseek函数一一对应.


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

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

相关文章

gitlab 迁移-安装-还原

文章目录 一、备份原有Gitlab1、备份清单2、备份执行 二、卸载删除原有Gitlab1、停止Gitlab2、卸载Gitlab3、查看Gitlab进程4、杀死进程5、删除所有包含Gitlab文件 三、安装Gitlab1、添加镜像地址2、安装依赖3、安装防火墙4、下载安装Gitlab5、配置Gitlab6、启动并访问 四、还原…

Linux基本指令(2.0)

周边知识&#xff1a; 1.Linux中&#xff0c; 一切皆文件 构建大文件 输入如下shell命令 i1; while [ $i -le 10000]; do echo "hello Linux $i"; let i; done 此时大文件已经创建在big.txt 此时我们发现cat查看无法查看开始内容 我们使用more 当占满一屏之后就不…

Unity-Shader - 2DSprite描边效果

实现一个简单的2D精灵图描边效果&#xff0c;效果如下 实现思路&#xff1a; 可以通过判断该像素周围是否有透明度为 0的值&#xff0c;如果有&#xff0c;则说明该像素位于边缘。 所以我们需要打开alpha blend&#xff0c;即&#xff1a; Blend SrcAlpha OneMinusSrcAlpha&am…

单实例应用程序

2023年12月6日&#xff0c;周三凌晨 什么是单实例应用程序 单实例应用程序可以确保在同一时间只有一个应用程序实例在运行。 通常情况下&#xff0c;当用户尝试再次启动一个已经启动过的应用程序时&#xff0c;操作系统会打开一个新的实例。但有些情况下&#xff0c;我们可能…

js中的栈(stack)和堆(heap)

什么是堆什么是栈&#xff1f; 程序运行时候&#xff0c;需要内存空间存放数据。系统划分出的两种内存空间就叫做stack&#xff08;栈&#xff09;和heap&#xff08;堆&#xff09;。 栈&#xff08;stack&#xff09;&#xff1a;由操作系统自动分配内存空间&#xff0c;自…

透明度值和注意点

透明度 透明度分为256阶&#xff08;0-255&#xff09;&#xff0c;计算机上用16进制表示为&#xff08;00-ff&#xff09;。透明就是0阶&#xff0c;不透明就是255阶,如果50%透明就是127阶&#xff08;256的一半当然是128&#xff0c;但因为是从0开始&#xff0c;所以实际上是…

react项目中使用video标签设置自动播放并未及时播放解决

react项目中使用video标签设置autoplay,但是视频不会直接播放&#xff0c;会加载一段时间后才会自动播放。 解决&#xff1a; 手动调用play方法 const videoRef useRef();useEffect(() > { if(videoRef?.current){if(videoRef?.current.paused){videoRef?.current.pla…

leetcode:1422. 分割字符串的最大得分(python3解法)

难度&#xff1a;简单 给你一个由若干 0 和 1 组成的字符串 s &#xff0c;请你计算并返回将该字符串分割成两个 非空 子字符串&#xff08;即 左 子字符串和 右 子字符串&#xff09;所能获得的最大得分。 「分割字符串的得分」为 左 子字符串中 0 的数量加上 右 子字符串中 1…

Android 12.0 Folder文件夹全屏后文件夹图标列表居中时拖拽app到桌面的优化

1.概述 在12.0的系统rom产品开发中,在Launcher3中在目前的产品需求开发中,对于Launcher3中的文件夹Folder的布局UI 进行了定制化的需求要求把Folder修改为全屏,然后在中间显示文件夹图标的列表,这时候如果Folder是全屏的话,如果拖拽文件夹列表中的app图标,只有拖拽 到屏…

html复习

html form表单作用是收集数据提交 input框体控件不在form不适用于表单提交 可编辑性:contenteditable 提示值消失&#xff1a;placeholder&#xff0c;value是初始数据 块标签&#xff1a;单独占有一个空间&#xff0c;独占一行&#xff0c;标签遵循从上到下排列。table、d…

UEC++ 探索虚幻5笔记(捡金币案例) day12

吃金币案例 创建金币逻辑 之前的MyActor_One.cpp&#xff0c;直接添加几个资源拿着就用 //静态网格UPROPERTY(VisibleAnywhere, BlueprintReadOnly)class UStaticMeshComponent* StaticMesh;//球形碰撞体UPROPERTY(VisibleAnywhere, BlueprintReadWrite)class USphereCompone…

【Linux知识点汇总】04 Linux软件包管理器RPM常用命令

RPM&#xff08;Red Hat Package Manager&#xff09;是一种用于在基于Red Hat的Linux发行版中安装、卸载、更新和管理软件包的工具 查看和显示命令 说明命令查看已安装的rpm包rpm -qa查询某个rpm包rpm -q pkg_name查看已安装rpm包提供的配置⽂件rpm -qc pkg_name查看⼀个包安…

【水】pytorch:torch.reshape和torch.Tensor.view的区别

【水】pytorch&#xff1a;torch.reshape和torch.Tensor.view的区别 注&#xff1a;本篇仅为学习笔记&#xff0c;请谨慎参考&#xff0c;如有错误请评论指出。 参考&#xff1a;Pytorch: view()和reshape()的区别&#xff1f;他们与continues()的关系是什么&#xff1f; 两者…

Flink流批一体计算(23):Flink SQL之多流kafka写入多个mysql sink

目录 1. 准备工作 生成数据 创建数据表 2. 创建数据表 创建数据源表 创建数据目标表 3. 计算 WITH子句 1. 准备工作 生成数据 source kafka json 数据格式 &#xff1a; topic case_kafka_mysql&#xff1a; {"ts": "20201011","id"…

JSON 语法详解:轻松掌握数据结构(上)

&#x1f90d; 前端开发工程师&#xff08;主业&#xff09;、技术博主&#xff08;副业&#xff09;、已过CET6 &#x1f368; 阿珊和她的猫_CSDN个人主页 &#x1f560; 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 &#x1f35a; 蓝桥云课签约作者、已在蓝桥云…

postgresql-effective_cache_size参数详解

在 PostgreSQL 中&#xff0c;effective_cache_size 是一个配置参数&#xff0c;用于告诉查询规划器关于系统中可用缓存的估计信息。这个参数并不表示实际的内存量&#xff0c;而是用于告诉 PostgreSQL 查询规划器系统中可用的磁盘缓存和操作系统级别的文件系统缓存的大小。它用…

Lambda表达式用法汇总

Lambda表达式用法汇总 java8 中引入的 Lambda 表达式真的是个好东西&#xff0c;掌握之后&#xff0c;写代码更简洁了&#xff0c;码字效率也提升了不少&#xff0c;这里咱 们一起来看看 Lambada 表达式常见的写法&#xff0c;加深理解。 1、有参无返回值函数式接口 8 种写法…

【代码随想录】算法训练计划39

dp 1、62. 不同路径 题目&#xff1a; 求路径方案多少个 思路&#xff1a; 这道题就有点dp了哈 func uniquePaths(m int, n int) int {//dp&#xff0c;写过,代表的是多少种// 初始化dp : make([][]int, m)for i : range dp {dp[i] make([]int, n)dp[i][0] 1 // 代表到…

用友NC Cloud FileParserServlet反序列化RCE漏洞复现

0x01 产品简介 用友 NC Cloud 是一种商业级的企业资源规划云平台,为企业提供全面的管理解决方案,包括财务管理、采购管理、销售管理、人力资源管理等功能,实现企业的数字化转型和业务流程优化。 0x02 漏洞概述 用友 NC Cloud FileParserServlet接口存在反序列化代码执行漏…

response应用

文章目录 [TOC](文章目录) response说明一、response文件下载二、待补充。。。 response说明 response是指HttpServletResponse,该响应有很多的应用&#xff0c;比如像浏览器输出消息&#xff0c;下载文件&#xff0c;实现验证码等。 一、response文件下载 1.创建一个javaw…