【Linux】文件操作(一)

news2024/11/26 12:40:59

目录

预备知识

复习C语言文件接口

fopen()

写入类:fwrite()、fprintf()、fputs()

读取类:fgets()

系统接口

open()

一个参数如何传递多个选项?

close()

write()

read()


 

预备知识

在正式讲解文件之前,我们需要有一些预备知识:


1.文件 = 文件内容 + 文件属性.


2.对文件操作:1.对内容操作 2.对属性操作


3.文件在磁盘(硬件)上,我们访问文件流程是什么呢?

先写访问文件的代码 ->编译->exe可执行程序->运行->访问到文件.

访问文件本质是进程在访问文件。而进程访问文件是需要接口的.我们目前所学到的接口都是一些语言接口,而不是系统接口.

磁盘是一种硬件,想要在硬件中写入,只有操作系统有权限。如果普通用户也想写入,那么操作系统必须提供文件系统调用的接口.

而我们平常使用的C语言的文件操作的相关接口都是对系统接口的封装,这样会使用户对接口更方便,简单的使用.

但是这样,会导致每个语言例如(C++和Iava)的文件操作的接口不相同,但是底层都是系统接口,因为这样的接口只有一套,只不过是封装方式不同。

封装的好处是可以跨平台运行代码(比如利用条件编译,动态裁剪),如果用户直接使用系统接口,换一个平台代码便运行不了了.


4.显示器是一种硬件,在linux下本质上是一个文件!printf向显示器打印,本质上也是一种写入;和在磁盘中向文件中写入是一样的!


5.Linux下,一切皆文件.我们现在对其只能有个理性的认识.

曾经我们理解的文件:read读取,write写入.

显示器:printf/cout ->一种写入

键盘:scanf/cin ->一种读取

以上都是站在我们写的程序的角度来说,将来我们的程序要加载到内存,相当于我们站在内存的角度,键盘相当于把我们的数据输交给内存(input),而内存把读取到的数据刷新到文件或者显示器当中(output)。

 这就相当于是一次IO操作.

 我们在回过头来,什么叫做文件呢?

站在系统的角度,能够被input读取,或者能够output写出的设备就叫做文件.

狭义上的文件:普通的磁盘文件

广义上的文件:显示器,网卡,声卡,显卡,磁盘等等,几乎所有的外设,都可称之为文件。    

复习C语言文件接口

fopen()

我们先使用man fopen 来查看一下函数的用法

 这个函数用来打开文件的,

        1.FIFE* 我们称为文件指针。

        2.其中第一个参数是要被打开函数的路径

        3.第二个参数是打开模式,即以什么样的方式打开.一共有6种:

r(读):对文件只进行读操作


r+(读和写):对文件进行读和写操作.


w(读):先把文件内容清空,然后从文件开始进行写入操作.若文件不存在,则创建一个新文件.


w+(读和写):先把文件内容清空,然后可以问对文件进行读和写操作,若文件不存在,则创建一个新文件。


a(写):向文件结尾处开始写,相当于追加操作。若文件不存在,则会创建一个新文件.

a+(读和写):从文件结尾处开始写,文件开头处开始读,若文件不存在,则会创建一个新文件.

r是读取文件,这个在我先讲解完w(写)之后,然后讲解会方便一些.

我们看一下w:首先在vim中编写如下代码

#include<stdio.h>    
    
int main()    
{    
  FILE* fp = fopen("log.txt","w");    
  if(fp == NULL)    
  {    
    perror("fopen");    
    return 1;    
  }    
  //进行文件操作                                                                                                                                       
  fclose(fp);    
                                                                                                                                             
  return 0;                                                                                                                                  
}     

 这个我们使用了w模式打开,并且我们当前路径下并没有“log.txt”这个文件.

我们退出make编译,然后运行

 这样因为原来文件不存在,所以会自动帮我们创建一个新文件.

那么问题是:我代码中只写了一个log.txt,那么是如何知道它在哪或者在哪创建呢?

当一个进程运行起来的时候,每个进程都会记录自己当前所处的工作路径!这个路径就叫做当前路径,log.txt的查找与创建都是在当前路径中进行的.


然后如果我们此时向log.txt文件中写入一些内容,然后再以w模式运行程序,会发生什么呢? 

以w模式打开文件 

 然后向文件中输入“hello,world”并运行. 

 我们发现一开始log.txt中还有内容,然后以w方式运行程序后,里面的内容就被清空了.

这就验证了我们刚才所说的w模式会在写入之前将文件内容清空.

那如果我们如果不想让文件被清空,而是接着文件后面的内容写呢?这个时候需要用a/a+模式进行打开.

这个写的接口fwrite我们马上讲,知道这个是向文件中写入就好了

然后我们退出来,编译运行: 

发现每一次运行并不会将文件清空,而是追加在后面继续写.

写入类:fwrite()、fprintf()、fputs()

我们刚才打开了文件,相应的就要对文件进行操作了,这是fwrite的用法:

         1.第一个参数ptr是指向要被写入的元素数组的指针。

         2.第二个参数size是每个元素的大小,字节为单位.

         3.第三个参数是元素的个数,每个元素的大小为size字节

         4.第四个参数为FILE指针,即fopen()打开文件之后返回的文件指针.

在代码中,我们这么写:

 

 因为s1是一个字符串,所以strlen直接计算了整个字符串的大小,所以第三个参数只传入1即可,大小为整个字符串的大小.

此时我们输出log.txt便成功写入到文件了

 


然后介绍一下fprintf()的用法,函数原型为:

​int fprintf(FILE *stream, const char *format, ...);

        1.第一个参数还是为文件指针。

        2.第二个参数就是我们正常的格式化输出。

用代码写入的时候,这样写

 然后便也成功的写入到文件当中了:


fputs同样地也是向文件中写入,函数原型是

int fputs(const char *s, FILE *stream);

        1.第一个参数为我们要写入的字符串内容.

        2.第二个参数还是文件指针.即一开始的fp.

我们用一下,也比较简单

 

 

 这样便成功写入到文件了.


读取类:fgets()

同样地,我们先来看一下函数原型:

char *fgets(char *s, int size, FILE *stream);

        1.第一个参数s是输出型参数,会把读取的结果存放在s指向的字符数组里.

        2.第二个参数size是读取的最大字节数,如果文件内容本身小于size,那么只读取文件内容,如果大于则读取size个字节

        3.第三个同样是文件指针,通过fopen()得到的.

看一下它的返回值:

 fgets()如果返回成功,则返回s,失败的话返回NULL.


具体是怎么使用呢?

 然后我们此时log.txt文件中是已经有内容的,我们运行程序便可以看到内容被输出了

 上面代码我们提到了stdout,这个是叫做标准输出流

执行程序时,我们C语言会默认打开三个标准输入输出流:

        1.stdin 标准输入,对应键盘

        2.stdout 标准输出,显示器

        3.stderr 标准错误, 显示器

我们用man stdin来看一下它.

 发现类型是文件指针,其实这也变相的说明了一个道理:linux下一切皆文件!

但是如何理解这些,还是个问题,我们讲解完系统接口之后,相信再回来理解就会透彻很多.

系统接口

这里系统接口一共有4个:open,close,read,write

open()

它的作用是打开 并且可能创建一个文件或设备.暂时理解为打开文件即可.

        1.第一个参数pathname,和刚才得接口一样,都是要打开的文件路径

        2.第二个参数flags是一些选项,它的选项非常多,这里会挑一些常用的讲解.

        3.第三个参数等下会说.

对于第二个参数:

flags中必须包含以上三个中的其中一个,分别代表O_RDONLY(read only只读),O_WRONLY(write only 只写),以及O_RDWR(read write读和写).

 然后后面可以再添加别的选项.那么只有一个flags参数,怎么传入很多的选项呢?

一个参数如何传递多个选项?

这里采用了位操作:即用int中不重复的一个bit位,就可以标识一种状态.

例如0000 0001可以表示一种状态,0000 0010表示第二种状态,以此类推。

当我们需要检测有没有第一种状态时,我们可以让flag此时&0x1(0000 0001),这个时候,如果结果为1,说明flag的最后一位有1,那么就代表它有这个选项,便执行对应的操作.

同样的,检测是否有第二种状态,只需继续判断flag&0x2(0000 0010)==1。如果等于,说明flag的倒数第二位有1,否则没有.

那么如何让flag同时拥有这两种状态呢(即如何同时传入两个选项)?

我们只需要让两个状态 |(按位或) 一下即可.假设

#define ONE 0X1 // 0000 0001
#define TWO 0X2 // 0000 0010

然后 flag = ONE | TWO,此时flag的位便是 0000 0011

然后我们再判断flag,此时flag & 0x1 = 1,说明有第一个选项;flag & 0x2 = 1,也说明有第二个选项,这样我们就相当于传入了多个选项了.


我们使用简易的代码来完成一下这个操作.

此时我们期待的结果应该是输出1,3和1,2,3.

此时便得到了我们想要的结果了,也顺便成就了一个参数里面传递多个选项.


同样地,回到open上,它的flags中很多选项虽然是是大写,但也是定义的宏,本质上也是数字,然后分别用不同的比特位标识.

然后我们再看一下open的返回值:

 可以看到open如果成功便返回一个新的文件描述符,失败则返回-1.

这个文件描述符我们下一章会详细讲解.现在你只要知道文件描述符是个整型即可.


知道了以上这些,我们开始用用open这系统接口吧.首先以只写的方式打开.

int main()    
{    
  int fd = open("log.txt",O_WRONLY);    
  if(fd < 0)    
  {    
    perror("open");    
    return 1;    
  }    
  //open successful    
  printf("open success,fd: %d\n",fd);    
}            

我们此时目录下并没有log.txt文件,c语言那个fopen接口,只写的话如果没有文件会给我们创建一个新的文件,那系统接口会吗?

我们编译运行一下:

我们发现说系统找不到这个文件,由此说明,系统只写这个接口是不会直接给我们创建新的文件的,而c语言之所以可以,是因为对系统接口进行了封装,简化了我们的使用.

其实你在应用层看到的一个很简单的动作,在系统接口或者OS层面,可能都需要做很多的工作

所以如果文件不存在想创建一个呢?这个时候便需要再传入一个选项O_CREATE.

然后我们将它按照上面所讲的,传递两个选项,则将两个选项 | 一下.

int fd = open("log.txt",O_WRONLY | O_CREAT);

 

此时我们便成功打开了.


但是,我们看一下我们创建好的文件的权限:

怎么会是这样的呢?我们不想要这样的,如果我们想指定权限该怎么做呢?

这就回到了我们刚开始讲open的时候第三个参数mode,它便是创建新文件时,赋予文件的权限.

比如我们给文件的权限是0666 .

int fd = open("log.txt",O_CREAT | O_WRONLY,0666); 

这样,这个权限总算看着正常一点,但是怎么会少一个呢?

我们理想的是-rw-rw-rw,这个怎么是-rw-rw-r--呢,少的一个w去哪了?

这是由于掩码umask的存在,我在之前文章的权限的理解里详细的说过。

默认掩码是002,而每个权限又不能和掩码中的比特位为1的位相同,002中2的比特位是010,不能和第二个值为1的位一样,所以只能将w权限去掉,因为w正是对应的第二个比特位.

我们可以在程序里,将这个进程的umask设为0,然后便解决了问题.

此时便得到了正确的结果:

close()

这个接口比较简单,就直接把文件描述符传进去,然后关闭了文件即可.

write()

先来看write的用法

 函数原型:

ssize_t write(int fd, const void *buf, size_t count);

        1.fd为open()返回的文件描述符.

        2.buf为要写入的内容

        3.count为向文件中写入多少字节.

我们直接用代码来演示:

 此时我们退出,编译运行

我们发现此时已经成功写入了. 

如果此时我们把写入的内容换一下:

然后再次运行:

我们发现原来的hello,write没有被清空,而abcd是直接从头开始写的,然后把原来的一部分内容给覆盖了.我们如果想清空内容,就需要用到一个新的选项:O_TRUNC

 

这个选项会讲原文件内容清空. 所以我们把open代码中加上这个选项:

int fd = open("log.txt",O_CREAT | O_WRONLY | O_TRUNC,0666); 

然后再次退出make编译运行:

发现此时原文件的内容已经被清空,然后写入了abcd.


如果我们想追加内容,即不清空,直接在文件后面继续写,我们把O_TRUNC换成O_APPEND即可. 这个便不再继续演示了.

read()

还是先来查看用法:

函数用法和write的非常类似,只不过把文件的内容读到buf,相当于buf成了输出型参数,读取的字节数是count.

最后read的内容会写在buffer中,然后我们最后输出它即可.

 我们在log.txt文件中编辑以下内容:

 然后编译运行程序:

 

 便成功读取出来了.

关于文件操作的一部分内容便到这结束了,下一章我们将真正迈入文件的范畴,开始讲解文件描述符等相关的一系列知识.

 

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

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

相关文章

C#中小数保留固定位数

我们写程序的时候&#xff0c;有时候数据想要对齐一点&#xff0c;如果小数位数不一样&#xff0c;自然就对不齐了。这里提供一个方法. 1.这里举例保留小数点后4位(不足4位后面补0)。 String result String.Format("{0:F4}", 123.456); 输出结果为result123.4560&a…

Linux离线安装mysql8.0+

文章目录 1.查看是否安装过MySQL2.MySQL卸载3.下载mysql4.上传mysql到指定目录5.解压MySQL安装包6.安装1.准备工作2.开始安装3.查看MySQL版本 7.修改my.cnf配置文件8.授权给mysql用户8.服务初始化10.启动MySQL11.登录12.修改密码13.设置远程登录1连接超时问题2确认网络3查看端口…

STL vector 详解

STL vector 详解 STL vector 详解 STL vector 详解一级目录二级目录三级目录 1. vector容器2. vector 容器的初始化函数1.初始化2.案例 3. vector的访问函数1. 成员函数访问2. 操作符[ ]访问3.案例 4. vector的插入函数1. 语法2. 案例 5. vector的删除函数1.语法2. 案例 6. vec…

Vue el-table 多表格联合显示、合并单元格

原型图 分析 先看内容是三个表&#xff0c;每个表的合并单元格都有点不同。 按照原型图给的内容&#xff0c;第一个是两列&#xff0c;有行合并和列合并&#xff0c;还有表头行合并。 现根据图造出mock数据&#xff0c;然后再写对应的代码。 export const columnVarsData {s…

数据结构day7(2023.7.21)

一、Xmind整理&#xff1a; 二、课上练习&#xff1a; 练习1&#xff1a;折半查找/二分查找 1-------100 key8850---10075-100int arr[]{12,23,33,45,66,78,99};key7912,23,33,45,66,78,990 6low mid high66, 78, 99mid1 mid highlow99lowhigh…

进程间的通信之管道(匿名管道)

文章目录 进程间通信&#xff08;IPC&#xff09;管道管道特点为什么可以使用管道进行进程间通信管道数据结构匿名管道的使用 管道实例管道读写特点管道设置非阻塞 进程间通信&#xff08;IPC&#xff09; inter process conmmunication &#x1f447;&#x1f447;&#x1f…

现场总线协议转换网关快速选型介绍

现场总线协议是一种用于设备间通信的标准化接口&#xff0c;它支持数字信号和信息在不同设备之间的传输&#xff0c;例如传感器、执行器、控制器等。捷米特总线协议网关支持Profinet/DeviceNet/EthernetIP/EtherCAT/RS485/Profibus/ModbusRTU/TCPIP/CAN/CANopen/CC-Link等工业网…

【教学类-36-09】20230722职业抽卡(midjounery-niji)(涂色、裁剪、游戏)

作品展示&#xff1a; 随机选学具&#xff0c;辨认职业名称、说说工作内容、涂色、裁剪、交换卡片等 灵感来源&#xff1a; 最近在网上搜索“midjounery 简笔画”&#xff0c;发现一条宝藏“关键词”——可以直接生成简笔画风格&#xff08;造型的外边框线加粗&#xff09;的样…

【Elasticsearch】ES简单快速入门

ES简单快速入门 概述 Elasticsearch是一个分布式全文搜索引擎 下载与安装 https://www.elastic.co/cn/downloads/elasticsearch下载之后 解压文件夹&#xff0c;进入bin目录&#xff0c;双击elasticsearch.bat文件 一开始初始化时间比较久&#xff0c;之后初始化之后&…

Istio Pilot源码学习(一):Pilot-Discovery启动流程、ConfigController配置规则发现

本文基于Istio 1.18.0版本进行源码学习 1、Pilot-Discovery工作原理 Pilot-Discovery是Istio控制面的核心&#xff0c;负责服务网格中的流量管理以及控制面和数据面之间的配置下发 Pilot-Discovery从注册中心&#xff08;如Kubernetes&#xff09;获取服务信息并汇集&#xff…

新版UI盲盒抽奖系统源码-带H5端小程序端可打包APP_带安装教程

新版UI盲盒抽奖系统源码-带H5端小程序端可打包APP,这套盲盒系统小白看了一下里面没有搭建教程的&#xff0c;但是盲盒的搭建方式都是差不多的这套就放给你们自己研究了&#xff0c;UI还是很好看的感兴趣可以自己搭建看看。

Vue中TodoList案例_勾选

与上一篇Vue中TodoList案例_添加有三个文件变化了 App.vue&#xff1a;中加了checkTodo方法 <template><div id"root"><div class"todo-container"><div class"todo-wrap"><MyHeader :addTodo"addTodo"/&…

【Linux】信号保存信号处理

前言&#xff1a;对信号产生的思考 上一篇博客所说的信号产生&#xff0c;最终都要有OS来进行执行&#xff0c;为什么&#xff1f;OS是进程的管理者&#xff01;信号的处理是否是立即处理的&#xff1f;在合适的时候 -》那什么是合适的时候&#xff1f;信号如图不是被立即处理…

动态规划入门第3课,经典DP问题2 --- 背包问题

练习1 第1题 方案数 查看测评数据信息 给你n个整数&#xff0c;每个数可选或不选&#xff0c;要求选一些数&#xff0c;使它们的和为S&#xff0c;问有多少种方案&#xff1f; 输入格式 第一行&#xff1a;2个整数n和s&#xff0c;范围都在[1, 100]。 第二行&#xff1a;n个…

spring boot3 集成swagger3

快速开始 | Knife14j 官方的推荐 1. 设置pom.xml 主要是引入nexus-maven&#xff0c;com.github.xiaoymin 2个&#xff0c;cn.hutool&#xff0c;org.springframework <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://ma…

Linux超详细的了解

文章目录 前言Linux的简介不同应用领域的主流操作系统Linux系统历史Linux系统各版本 Linux的安装安装方式介绍安装Linux网卡设置安装SSH连接工具finalShell连接虚拟机Linux和Windows目录结构对比Linux目录介绍 Linux常用命令Linux命令初体验文件目录命令ls命令cd命令cat命令mor…

【Hammerstein模型的级联】快速估计构成一连串哈默斯坦模型的结构元素研究(Matlab代码实现)

&#x1f4a5;1 概述 在许多振动应用中&#xff0c;所研究的系统略微非线性。Hammerstein模型的级联可以方便地描述这样的系统。Hammerstein提供了一种基于指数正弦扫描相位属性的简单方法。 构成一连串Hammerstein模型的结构元素可以在快速估计中起到关键的作用。Hammerstei…

260. 只出现一次的数字 III

题目描述&#xff1a; 主要思路&#xff1a; 首先通过抑或的方式可以将所有两个的数字全部排除&#xff0c;得到两个单个数字的异或值。 接下来将当前得到的异或值取最低一位的1。 分析异或值的每一位&#xff0c;为1的肯定是两个数中一个有一个没有。于是可以通过这一特性将两…

(转载)PID神经元网络解耦控制算法(matlab实现)

​本博客的完整代码获取&#xff1a;https://www.mathworks.com/academia/books/book106283.html​ 1案例背景 1.1PID 神经元网络结构 PID神经元网络从结构上可以分为输人层、隐含层和输出层三层&#xff0c;n个控制量的PID神经元网络包含n个并列的相同子网络,各子网络间既相…

【车载开发系列】AUTOSAR DemComponent和DemDTC

【车载开发系列】AUTOSAR DemComponent和DemDTC 【车载开发系列】AUTOSAR DemComponent和DemDTC 【车载开发系列】AUTOSAR DemComponent和DemDTC一. DemComponent概念二. DemDTC概念三. 常用设置参数DemDTCClass1&#xff09; DemDTCFunctional2&#xff09;DemDTCSeverity3&am…