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.rhkb.cn/news/210726.html

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

相关文章

【面试】Java最新面试题资深开发-JVM第一弹

问题一&#xff1a;Java中的垃圾回收机制 在Java中&#xff0c;垃圾回收是如何工作的&#xff0c;可以简要描述一下垃圾回收的算法有哪些吗&#xff1f; 在Java中&#xff0c;垃圾回收是一种自动管理内存的机制&#xff0c;它负责识别不再被程序引用的对象并释放其占用的内存…

HarmonyOS与AbilitySlice路由配置

上一章我有教到鸿蒙应用开发——Ability鸿蒙应用开发的基础知识&#xff0c;那么今天我们来讲一下AbilitySlice路由配置 AbilitySlice路由配置 虽然一个Page可以包含多个AbilitySlice&#xff0c;但是Page进入前台时界面默认只展示一个AbilitySlice。默认展示的AbilitySlice是…

Java+SSM springboot+MySQL家政服务预约网站设计en24b

随着社区居民对生活品质的追求以及社会老龄化的加剧&#xff0c;社区居民对家政服务的需求越来越多&#xff0c;家政服务业逐渐成为政府推动、扶持和建设的重点行业。家政服务信息化有助于提高社区家政服务的工作效率和质量。 本次开发的家政服务网站是一个面向社区的家政服务网…

TCP首部格式_基本知识

TCP首部格式 表格索引: 源端口目的端口 序号 确认号 数据偏移保留 ACK等 窗口检验和紧急指针 TCP报文段首部格式图 源端口与目的端口: 各占16位 序号:占32比特&#xff0c;取值范围0~232-1。当序号增加到最后一个时&#xff0c;下一个序号又回到0。用来指出本TCP报文段数据载…

面试 Java 框架八股文十问十答第二期

面试 Java 框架八股文十问十答第二期 作者&#xff1a;程序员小白条&#xff0c;个人博客 相信看了本文后&#xff0c;对你的面试是有一定帮助的&#xff01; ⭐点赞⭐收藏⭐不迷路&#xff01;⭐ 1.AOP的术语&#xff0c;以及两种动态代理实现方法&#xff0c;以及它们的区别…

Notepad++批量添加引号

工作中经常会遇到这样情景&#xff1a;业务给到一批订单号&#xff0c;需要查询这批订单的某些字段信息。在where条件中需要传入这些订单号的数组&#xff0c;并且订单号用引号引起&#xff0c;用引号隔开。 字符串之间长度相同 可以按住CtrlAlt和鼠标左键选中区域&#xff0…

手持式安卓主板_PDA安卓板_智能手持终端方案

手持式安卓主板方案是一种智能终端设备&#xff0c;具备自动对焦和闪光灯功能&#xff0c;可以在昏暗的环境下快速扫描二维码并轻松采集数据。该方案还提供多渠道支付和数据采集功能&#xff0c;为用户提供了便捷的体验。 该方案的产品基于手持式安卓主板&#xff0c;并搭载了八…

基于ROPNet项目训练modelnet40数据集进行3d点云的配置

项目地址&#xff1a; https://github.com/zhulf0804/ROPNet 在 MVP Registration Challenge (ICCV Workshop 2021)&#xff08;ICCV Workshop 2021&#xff09;中获得了第二名。项目可以在win10环境下运行。 论文地址&#xff1a; https://arxiv.org/abs/2107.02583 网络简介…

基于H5“汉函谷关起点新安县旅游信息系统”设计与实现

目 录 摘 要 1 ABSTRACT 2 第1章 绪论 3 1.1 系统开发背景及意义 3 1.2 系统开发的目标 3 第2章 主要开发技术介绍 5 2.1 H5技术介绍 5 2.2 Visual Studio 技术介绍 5 2.3 SQL Server数据库技术介绍 6 第3章 系统分析与设计 7 3.1 可行性分析 7 3.1.1 技术可行性 7 3.1.2 操作…

HTTP请求

前言 HTTP是应用层的一个协议。实际我们访问一个网页&#xff0c;都会像该网页的服务器发送HTTP请求&#xff0c;服务器解析HTTP请求&#xff0c;返回HTTP响应。如此就是我们获取资源或者上传资源的原理 HTTP请求报头格式 图片来自网络 HTTP请求报头总体有四部分&#xff1a;…

pycharm中绘制一个3D曲线

import numpy as np import matplotlib.pyplot as plt # 中文的设置 import matplotlib as mp1 from mpl_toolkits.mplot3d import Axes3D mp1.rcParams["font.sans-serif"] ["kaiti"] mp1.rcParams["axes.unicode_minus"] False # 数据创建 X…

忽略python运行出现的大量警告

添加以下代码即可 import warnings warnings.filterwarnings(ignore)

制作红木家具3d模型

在线工具推荐&#xff1a; 3D数字孪生场景编辑器 - GLTF/GLB材质纹理编辑器 - 3D模型在线转换 - Three.js AI自动纹理开发包 - YOLO 虚幻合成数据生成器 - 三维模型预览图生成器 - 3D模型语义搜索引擎 在家居行业中&#xff0c;设计师可以通过在3D建模中添加实际的家具、…

【python】包(package)与模块(module)、import、__name__与__main__

导入模块一般写在程序最前面&#xff0c;且顺序为&#xff1a;内置模块、第三方模块、自定义模块 一、模块&#xff08;module&#xff09;与包&#xff08;package&#xff09; 模块&#xff08;module&#xff09;可以理解为是一个.py文件&#xff0c;import 模块 相当于执行…

应用于智慧园区的AI边缘计算盒子+AI算法软硬一体化方案

工业园区多为生产型和物流型企业&#xff0c;劳动人员密集&#xff0c;外来人口多&#xff0c;农民工多&#xff0c;人员流动大&#xff0c;车流量大&#xff0c;易引发车祸、破坏公共设施和绿化工程等案件; 英码智慧园区方案&#xff0c;可实现100%管理所有出入人员&#xff1…

ViVo小游戏对接sdk

1.安装环境&#xff1a; 电脑环境&#xff1a;adb环境和oppo一样&#xff0c;npm环境和oppo一样 升级npm&#xff1a; npm install -g npm 清除npm缓存&#xff1a;npm cache clean -f 安装vivo初始化小游戏的工具&#xff1a; npm install -g vivo-minigame/cli 解决办法&…

【Linux】:信号(三)捕捉

信号捕捉 一.sigaction1.基本使用2.sa_mask字段 二.可重入函数三.volatile四.SIGCHLD信号 承接上文 果信号的处理动作是用户自定义函数,在信号递达时就调用这个函数,这称为捕捉信号。由于信号处理函数的代码是在用户空间的,处理过程比较复杂,举例如下: 用户程序注册了SIGQUIT信…

Lambda表达式与方法引用

作者简介&#xff1a;大家好&#xff0c;我是smart哥&#xff0c;前中兴通讯、美团架构师&#xff0c;现某互联网公司CTO 联系qq&#xff1a;184480602&#xff0c;加我进群&#xff0c;大家一起学习&#xff0c;一起进步&#xff0c;一起对抗互联网寒冬 引子 先来看一个案例 …

Vue3获取阴历/农历日期

安装插件 pnpm add chinese-lunar-calendar引入阳历/阴历切换函数 import {getLunar} from chinese-lunar-calendarexport function lunarDate(pDate){const year pDate.getFullYear()const month pDate.getMonth() 1const day pDate.getDate()const result getLunar(yea…

房屋租赁出售经纪人入驻小程序平台

一款专为房屋中介开发的小程序平台&#xff0c;支持独立部署&#xff0c;源码交付&#xff0c;数据安全无忧。 核心功能&#xff1a;房屋出租、经纪人独立后台、分佣后台、楼盘展示、房型展示、在线咨询、地址位置配套设施展示。 程序已被很多房屋交易中介体验使用过&#x…