[Linux]:文件(上)

news2024/12/26 0:11:27

img

✨✨ 欢迎大家来到贝蒂大讲堂✨✨

🎈🎈养成好习惯,先赞后看哦~🎈🎈

所属专栏:Linux学习
贝蒂的主页:Betty’s blog

1. C语言文件操作

C语言文件操作接口如下,详情可参照——C语言文件

文件操作函数功能
fopen打开文件
fclose关闭文件
fputc写入一个字符
fgetc读取一个字符
fputs写入一个字符串
fgets读取一个字符串
fprintf格式化写入数据
fscanf格式化读取数据
fwrite向二进制文件写入数据
fread从二进制文件读取数据
fseek设置文件指针的位置
ftell计算当前文件指针相对于起始位置的偏移量
rewind设置文件指针到文件的起始位置
ferror判断文件操作过程中是否发生错误
feof判断文件指针是否读取到文件末尾

读写方式如下:

文件使用方式含义如果指定文件不存在
“r”(只读)为了输入数据,打开一个已经存在的文本文件出错
“w”(只写)为了输出数据,打开一个文本文件建立一个新的文件
“a”(追加)向文本文件尾添加数据出错
“rb”(只读)为了输入数据,打开一个二进制文件出错
“wb”(只写)为了输出数据,打开一个二进制文件建立一个新的文件
“ab”(追加)向一个二进制文件尾添加数据出错
“r+”(读写)为了读和写,打开一个文本文件出错
“w+”(读写)为了读和写,建议一个新的文件建立一个新的文件
“a+”(读写)打开一个文件,在文件尾进行读写建立一个新的文件
“rb+”(读写)为了读和写打开一个二进制文件出错
“wb+”(读写)为了读和写,新建一个新的二进制文件建立一个新的文件
“ab+”(读写)打开一个二进制文件,在文件尾进行读和写建立一个新的文件

下面是一个使用C语言文件的示例:

#include<stdio.h>
int main()
{
    FILE*fp=fopen("log.txt","w");
    if(fp==NULL)
    {
        perror("fopen fail:");
        return 1;
    }
    //open success
    const char*msg="hello betty!\n";
    int count=5;
    while(count--)
    {
        fputs(msg,fp);
    }
    fclose(fp);
    return 0;
}

一般而言如果没有定义对应的log.txt文件,系统会在当前路径自动创建该文件。并且当前路径并不是指可执行程序所处的路径,而是指该可执行程序运行成为进程时我们所处的路径。比如我们可以在上级目录执行testfile文件:

可以看见log.txt是在该对应路径创建的,而不是对应可执行文件所在目录创建的。

其中我们也可以通过监视进程的方式,观察一下:

然后我们可以看见两个软连接cwdexe,分别对应的就是进程运行时我们所处的路径,以及可执行文件所处路径

2. 三个默认打开流

我们常说Linux下一切皆文件,那么我们的键盘与显示器自然也是文件。我们向键盘输入数据,本质就是操作系统向键盘文件中读取数据;我们能从显示器看见数据,本质就是操作系统向显示器文件写入数据。但是我们在使用键盘与显示器时并没有手动进行任何文件相关的读写操作,那我们又是如何对键盘文件与显示器文件进行读写的呢?

答案自然是操作系统自动帮我们打开的,任何进程在运行时,操作系统都会默认打开三个输入输出流,分别为:标准输入流标准输出流以及标准错误流。对于C语言分别就是:stdinstdout以及stderr。对于C++分别就是:cincoutcerr,自然其他语言也会有相似的概念,因为这是操作系统所支持的,而不是某个语言所独有的。

我们可以在Linux中的man查看对应的声明:

其中标准输入流对应的就是我们的键盘,而标准输出流与标准错误流对应的就是我们显示器。

其中我们也可以通过fputs函数验证一下:

#include<stdio.h>
int main()
{
  //向显示器打印
  fputs("hello betty!\n",stdout);
  fputs("hello betty!\n",stdout);
  fputs("hello betty!\n",stdout);
  fputs("hello betty!\n",stdout);
  return 0;
}

3. 系统文件I/O

在前面我们学习操作系统时知道,为了方便用户使用,一般我们会对系统接口进行封装。我们的文件操作也不例外,像fopenfclose等接口本质其实对操作系统提供的文件接口的封装。接下来我们就来学习一下系统提供的文件接口。

3.1 open函数

首先我们来介绍文件打开操作的系统接口。

  • pathname:表示打开或者创建的目标文件,若pathname以路径的方式给出,则当需要创建该文件时,就在pathname路径下进行创建。若pathname以文件名的方式给出,则当需要创建该文件时,默认在当前路径下进行创建。
  • ·flags:表示打开文件的方式。
  • mode:表示创建文件的默认权限(八进制数)。

其中常用文件打开方式有如下几个:

参数选项含义
O_RDONLY以只读的方式打开文件
O_WRNOLY以只写的方式打开文件
O_APPEND以追加的方式打开文件
O_RDWR以读写的方式打开文件
O_CREAT当目标文件不存在时,创建文件

如果想同时兼具多个打开方式,可以使用逻辑与|链接两个选项。比如说我们想打开文件并且文件不存在时创建文件,可以写成:

O_WRNOLY|O_CREAT

这些选项本质也就是一个宏定义,其中flags是一个整型,若将一个比特位作为一个标志位,则理论上flags可以传递32种不同的标志位。

所以我们也可以使用按位与&操作来检测是否设置某个选项:

if (flags&O_RDONLY){
    //设置了O_RDONLY选项
}
if (flags&O_WRONLY){
    //设置了O_WRONLY选项
}
if (flags&O_RDWR){
    //设置了O_RDWR选项
}
if (flags&O_CREAT){
    //设置了O_CREAT选项
}
//...

并且如果我们打开的文件已存在就使用第一个接口(两个参数),如果打开的文件不存在就需要使用第二个接口(三个参数),即需要为创建的文件设置默认权限。

如果我们要为文件设置默认权限,就需要考虑文件默认掩码umask的影响。我们之前讲过文件的默认权限为:mode&(~mask),我们除了可以在命令行通过指令umask 八进制数来修改默认的掩码umask(默认为002)外,还能在程序中调用umask函数进行修改。比如我们将umask设置为0:

umask(0); //将文件默认掩码设置为0

最后再来探究一下open的返回值,也就是文件描述符fd

#include<stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main()
{
    umask(0);//设置文件掩码为0
    int fd1 = open("log1.txt", O_RDONLY | O_CREAT, 0666);
	int fd2 = open("log2.txt", O_RDONLY | O_CREAT, 0666);
	int fd3 = open("log3.txt", O_RDONLY | O_CREAT, 0666);
	int fd4 = open("log4.txt", O_RDONLY | O_CREAT, 0666);
	int fd5 = open("log5.txt", O_RDONLY | O_CREAT, 0666);
	printf("fd1:%d\n", fd1);
	printf("fd2:%d\n", fd2);
	printf("fd3:%d\n", fd3);
	printf("fd4:%d\n", fd4);
	printf("fd5:%d\n", fd5);
    return 0;
}

运行之后我观察到文件描述符是从3开始的,并且依次递增,这起始并不是偶然。至于为什么,我们等会儿在揭晓。

当然这只是文件成功返回的情况,如果文件打开失败,那将返回-1。

3.2 close函数

我们可以调用系统接口close来关闭指定文件,其原型为:

int close(int fd);

使用close函数时传入需要关闭文件的文件描述符即可,若关闭文件成功则返回0,若关闭文件失败则返回-1。

3.3 write函数

同样我们也能通过系统接口write对文件进行写入,其原型为:

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

其中fd指的是文件描述符,buf为用户缓冲区,而count为期望写的字节数。如果写入成功返回实际写入的字节数,若写入失败则返回-1。

注意:ssize_t其实就是一个有符号整型,具体来说就是被typedef重新定义过:typedef int ssize_t

以下我们可以利用write函数对一个log.txt文件进行写入:

#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<string.h>
int main()
{
    int fd=open("log.txt",O_WRONLY|O_CREAT);
    if(fd<0)
    {
        //open error
        perror("open fail:");
        return 1;
    }
    const char*msg="hello betty!\n";
    for(int i=0;i<8;i++)
    {
        write(fd,msg,strlen(msg));
    }
    close(fd);
    return 0;
}

3.4 read函数

同样我们也能通过系统接口read对文件进行读写,其原型为:

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

其中fd指的是文件描述符,buf为用户缓冲区,而count为期望读的字节数。如果读出成功返回实际读出的字节数,若读出失败则返回-1。

以下我们可以利用read函数对一个log.txt文件进行读出:

#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<string.h>
int main()
{
    int fd=open("log.txt",O_RDONLY);
    if(fd<0)
    {
        perror("open fail:");
        return 1;
    }
    char buf[1024]={'\0'};
    ssize_t ret=read(fd,buf,1023);
    if(ret>0)
    printf("%s",buf);
    close(fd);
    return 0;
}

4. 文件描述符——fd

在我们的操作系统中,文件是由我们进程所打开的,存在大量进程就意味着存在大量被打开的文件。为了方便我们对文件进行管理,我们就将每个文件struct file链入我们的双向链表之中。

struct File
{
  //包含了打开文件的相关属性
  //链接属性
};

而一个文件也可能被多个进程所读写,为了让操作系统能够准确识别每个进程对应的文件,我们就一定要让进程与我们的文件建立联系。事实也是如此,我们的进程控制块task_struct中就存在一个指针指向一个名为struct file_struct的结构体,这个结构体中存在一个结构体指针数组struct file*fd_array[]分别存放着着每个文件struct file的地址。这样我们的进程就与文件建立起了联系。

画板

一般我们的指针数组struct file*fd_array[]的0,1,2下标分别对应我们的标准输入流,标准输出流,标准错误流这三个文件,而这些下标就是我们所说的文件描述符——fd。这也解释了我们打开文件的描述符为什么从3开始,并且依次递增。并且,通过对应的文件描述符,进程只需要找到对应的指针数组fd_array就能访问对应的文件,这也是为什么我们文件的系统调用接口的参数一定会有fd的原因。

当然如果我们在中途关掉某个文件,操作系统就会为该下标重新分配对应的文件。

#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
int main()
{
    close(0);
    close(2);
	int fd1 = open("log1.txt", O_RDONLY | O_CREAT, 0666);
	int fd2 = open("log2.txt", O_RDONLY | O_CREAT, 0666);
	int fd3 = open("log3.txt", O_RDONLY | O_CREAT, 0666);
	int fd4 = open("log4.txt", O_RDONLY | O_CREAT, 0666);
	int fd5 = open("log5.txt", O_RDONLY | O_CREAT, 0666);
	printf("fd1:%d\n", fd1);
	printf("fd2:%d\n", fd2);
	printf("fd3:%d\n", fd3);
	printf("fd4:%d\n", fd4);
	printf("fd5:%d\n", fd5);
	return 0;
}

我们也知道,当一个程序运行起来时,操作系统会将该程序的代码和数据加载到内存,然后为其创建对应的task_structmm_struct、页表等相关的数据结构,并通过页表建立虚拟内存和物理内存之间的映射关系。如果与我们的文件管理联系起来,就是一个磁盘文件log.txt加载进内存形成内存文件,最后加入对应双向链表中管理起来。

画板

当文件存储在磁盘上时,我们称之为磁盘文件。而当磁盘文件被加载到内存中后,就变成了内存文件。磁盘文件与内存文件的关系,恰似程序和进程的关系。程序在运行起来后成为进程,同样,磁盘文件在加载到内存后成为内存文件。磁盘文件主要由两部分构成,即文件内容文件属性。文件内容指的是文件中存储的数据,而文件属性则是文件的一些基本信息,包括文件名、文件大小以及文件创建时间等。这些文件属性也被称为元信息。在文件加载到内存的过程中,一般会先加载文件的属性信息。这是因为在很多情况下,我们可能只需要了解文件的基本属性,而不一定立即需要对文件内容进行操作。当确实需要对文件内容进行读取、输入或输出等操作时,才会延后式地加载文件数据。这样的设计可以提高系统的效率,避免在不必要的时候浪费资源加载大量的文件数据。

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

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

相关文章

零基础国产GD32单片机编程入门(十九)红外避障传感器模块实战含源码

文章目录 一.概要二.红外避障模块主要参数1.模块引脚定义2.模块特点3.模块原理图 三.GD32单片机红外避障模块检测实验四.工程源代码下载五.小结 一.概要 红外避障模块具有一对红外线发射与接收管&#xff0c;发射管发射出一定频率的红外线&#xff0c;当检测遇到障碍物时&…

HTML5( HTML5 、CSS3) 第一天

HTML5 第一天 HTML5 第一天一、什么是 HTML5二、HTML5 新增标签三、多媒体音频标签四、多媒体视频标签五、新增 input 标签六、新增表单属性七、CSS3 新特性八、CSS3 属性选择器九、结构伪类选择器十、nth-child 参数详解十一、nth-child 和 nt-of-type 的区别十二、伪元素选…

写作翻译两不误!Deepl翻译,我的创意加速器

嘿&#xff0c;朋友们&#xff01;有没有觉得有时候写论文的摘要、给外国朋友发邮件&#xff0c;或者追剧的时候没有字幕&#xff0c;这些都挺让人头疼的&#xff1f;就像是在猜谜语一样难搞&#xff1f;别担心&#xff0c;我有个好消息要告诉大家&#xff0c;2024年翻译界出了…

azkaban安装

azkaban安装 Azkaban介绍azkaban三大组件 Azkaban安装1、下载地址2、安装前准备3、安装1、分别解压web-server、executor-server2、初始化脚本 4、配置ssl认证5、配置 execServer6、配置web-server7、配置user8、启动、激活 验证 Azkaban介绍 Azkaban是由Linkedin开源的一个批…

【西电电装实习】3. SMT

前言 西电电装实习 定义 SMT&#xff0c;全称为Surface Mount Technology&#xff0c;即表面贴装技术。 是一种将电子元器件直接贴装在印刷电路板&#xff08;PCB&#xff09;表面的工艺。 与传统的插针式组装方法相比&#xff0c;SMT技术具有更高的组装密度、更小的电子产品…

excel提示宏病毒处理

excel提示宏病毒如何解决&#xff1f; 文章目录 操作步骤1、打开文件2、找到选项3、点击信用中心4、点击信任中心设置5. 依次点击&#xff1a;点击宏设置→通过通知禁用 VBA 宏&#xff08;A&#xff09;→&#xff08;去掉√&#xff09;信任对VBA工程对象模型的访问→确定6、…

MySQL总结(上)

目录 一、SQL语句1.1、DDL&#xff08;数据库定义语言&#xff09;1.1.1、定义数据库1.1.2、定义数据表 1.2、DML&#xff08;数据库操作语言&#xff09;1.2.1、增加 insert into1.2.2、删除 delete1.2.3、修改 update 1.3、DQL&#xff08;数据库查询语言&#xff09;1.3.1、…

info_scan!自动化漏洞扫描系统,附下载链接

在我们团队的日常工作中&#xff0c;定期进行安全演练和漏洞扫描几乎是必不可少的。每次安全互动我们都需要对关键资产进行全面的安全评估&#xff0c;及时发现可能存在的安全隐患。 就在上周&#xff0c;我们针对几个主要服务进行了例行的漏洞扫描。在这个过程中&#xff0c;…

【数学分析笔记】第3章第2节 连续函数(3)

3. 函数极限与连续函数 3.2 连续函数 【Riemann&#xff08;黎曼&#xff09;函数】 R ( x ) { 0 , x 是无理数 1 p , x q p , p ∈ N , q ∈ Z 且 q ≠ 0 , p 与 q 互质 1 , x 0 R(x)\left\{\begin{matrix} 0&,x是无理数 \\ \frac{1}{p}&,x\frac{q}{p},p\in\mat…

你真的懂吗系列——GPIO

你真的懂吗 文章目录 你真的懂吗前言一、GPIO介绍二、GPIO基本结构三、GPIO的八种模式浮空输入输入上拉输入下拉模拟输入开漏输出推挽输出什么是推挽结构和推挽电路&#xff1f;开漏输出和推挽输出的区别&#xff1f;开漏式复用推挽式复用 前言 最近在做STM32的时候发现有些寄…

利用 Zero-1-2-3 进行多视图 3D 重建:从单图像到多视图 3D 模型的生成

3D 模型生成在计算机视觉领域有着广泛的应用&#xff0c;从虚拟现实到自动驾驶&#xff0c;基于单张图像的 3D 重建技术正在迅速发展。这篇博客将带你深入探索如何使用 Zero-1-2-3 框架进行多视图 3D 重建&#xff0c;通过详细解析该框架中的代码结构和功能&#xff0c;帮助你理…

BUUCTF 之Basic 1(BUU LFI COURSE 1)

1、启动靶场&#xff0c;会生成一个URL地址&#xff0c;打开给的URL地址&#xff0c;会看到一个如下界面 可以看到是一个PHP文件&#xff0c;非常的简单&#xff0c;就几行代码&#xff0c;判断一下是否有一个GET的参数&#xff0c;并且是file名字&#xff0c;如果是并且加载&a…

C语言 预处理详解(二) #命令行定义 #条件编译 #文件包含 #其他预处理指令

文章目录 前言 一、命令行定义 二、条件编译 三、文件包含 什么叫做文件包含&#xff1f; (一)、本地文件包含 (二)、库文件包含 如何做才能避免头文件被多次包含呢&#xff1f; 方法一&#xff1a;利用条件编译&#xff1a; 方法二&#xff1a;利用 #pragma once 四、其他预处…

鸿蒙界面开发——组件(8):图形绘制

绘制几何图形——父组件Shape 绘制组件的父组件&#xff0c;父组件中会描述所有绘制组件均支持的通用属性。 1、绘制组件使用Shape作为父组件&#xff0c;实现类似SVG的效果。 2、绘制组件单独使用&#xff0c;用于在页面上绘制指定的图形。 Shape(value?: PixelMap) value …

指针之旅(4)—— 指针与函数:函数指针、转移表、回调函数

目录 1. 函数名的理解 1.1 “函数名”和“&函数名”的含义 1.2 函数(名)的数据类型 2. 函数指针(变量) 2.1 函数指针(变量)的创建格式 2.2 函数指针(变量)的使用格式 2.3 例子 判别 3. typedef 关键字 3.1 typedef的作用 3.2 typedef的运作逻辑 和 函数指针类型…

全球瞩目丨2024深圳秋季糖酒会火热招商中

第 111 届深圳秋季全国糖酒会 2024 年 10 月29-31日 将在深圳国际会展中心&#xff08;宝安新馆&#xff09;盛大举行。 这是一场备受瞩目的行业盛会&#xff0c; 为企业提供了一个展示产品、 拓展市场、加强合作的绝佳机会。 作为亚洲地区食品行业规模最大、最具影响力的…

stm32之硬件SPI读写W25Q64存储器应用案例

系列文章目录 1. stm32之SPI通信协议 2. stm32之软件SPI读写W25Q64存储器应用案例 3. stm32之SPI通信外设 文章目录 系列文章目录前言一、电路接线图二、应用案例代码三、应用案例代码分析3.1 基本思路3.2 相关库函数介绍3.3 MySPI模块3.3.1 模块初始化3.3.2 SPI基本时序单元模…

指挥中心操作台厂家哪家好?选择时需要注意哪些?

在构建高效、稳定的指挥中心过程中&#xff0c;操作台作为核心设备之一&#xff0c;其选择至关重要。面对市场上琳琅满目的指挥中心操作台厂家&#xff0c;如何挑选出既符合需求又品质卓越的合作伙伴&#xff0c;成为众多采购者关注的焦点。接下来就给大家从以下几个方面探讨指…

uniapp设置隐藏qiun-data-charts数据标签

隐藏前&#xff1a; 隐藏后&#xff1a; 具体代码实现&#xff1a; 在opts配置中传入 "dataLabel": false 即可

个性化推荐兴趣社交社交平台

1 项目介绍 社交兴趣平台是一个基于 spring boot、vue3 的社交平台&#xff0c;旨在为用户提供一个分享、交流和发现各种有趣内容的场所。 该平台的核心功能是让用户能够创建个人主页并发布自己的动态、经历、见解和创意。用户可以自由发表各种主题的内容&#xff0c;涵盖但不…