linux基础IO【文件操作】

news2025/6/24 21:06:10

 

目录

前言:

1.文件理解

2.C语言文件操作

2.1文件打开

2.2文件关闭

2.3文件写入

2.4文件读取

3.系统文件操作 

3.1open 

3.2close

3.3write

 3.4read

 4.访问文件本质



前言:

我们在学习文件操作之前先要了解文件的构成,文件 = 内容+ 属性,对于打开的文件,是谁打开的?我们要能回顾到上个章节的进程,进程和文件的关系又是什么样?对于没打开的文件在哪里呢?如何存储?

对于打开的文件首先要预加载到内存,进程和文件的关系肯定是1:n,操作系统要对其做管理,我们在初学进程的时候就知道,任何用户都不能跳过操作系统直接访问硬件,所以用户需要调用库函数使用系统接口访问,如下图所示:

1.文件理解

文件操作的本质是什么?

  • 语言层面的文件操作就是直接使用库函数,而事实上,文件操作是系统层面的问题,就像进程管理一样,OS也会通过 先描述,再组织 的方式对文件进行管理、操作

文件由什么构成?一般文件放在哪里?

  • 文件 = 内容 + 属性
  • 未使用的文件位于 磁盘,而使用中的文件 属性 会被加载至内存中
  • 本文讨论的是已被加载至内存文件的相关操作

系统是如何区分文件的?

  • 文件可以同时被多次使用,OS 为了管理好文件,会像使用 task_struct 管理进程一样,通过 struct file 存储文件属性进行管理
  • struct file 结构体包含了文件位置信息、权限信息和缓冲区信息,引用计数

 


2.C语言文件操作

 先会议一下c文件的接口,我们都知道c程序默认启动的时候,会打开三个标准输入输出流(文件)

#include <stdio.h>

extern FILE *stdin;//标准输入
extern FILE *stdout;//标准输出
extern FILE *stderr;//标准错误
 

我们以此为基础复盘一下以下函数

2.1文件打开

 

FILE * fopen ( const char * filename, const char * mode );

 通过文件名以指定打开方式,打开文件

打开方式(参数2)

  • w 只写,如果文件不存在,会新建,文件写入前,会先清空内容然后在文件开始写入
  • a 追加,在文件末尾,对文件进行追加写入,追加前不会清空内容
  • r 只读,打开已存在的文件进行读取,若文件不存在,会打开失败
  • w+a+r+ 读写兼具,区别在于是否会新建文件,只有 r+ 不会新建
//打开文件进行操作
//在当前目录中打开文件 log.txt
//注意:同一个文件,可以同时多次打开
FILE* fp1 = fopen("log.txt", "w");	//只读
FILE* fp2 = fopen("log.txt", "a");	//追加
FILE* fp3 = fopen("log.txt", "r");	//只写,文件不存在会打开失败

FILE* fp4 = fopen("log.txt", "w+");	//可读可写
FILE* fp5 = fopen("log.txt", "a+");	//可读可追加
FILE* fp6 = fopen("log.txt", "r+");	//可读可写,文件不存在会打开失败

 

若文件打开失败,会返回空 NULL,可以在打开后判断是否成功

注意: 若参数1直接使用文件名,则此文件需要位于当前程序目录下,如果想指定目录存放,可以使用绝对路径 或者在程序中使用chdir函数更改路径cwd

2.2文件关闭

 文件打开并使用后需要关闭,就像动态内存申请后需要释放一样

int fclose ( FILE * stream );

 关闭已打开文件,只需通过 FILE* 指针进行操作即可

//对上面打开的文件进行关闭
//无论以哪种方式打开,关闭方法都一样
fclose(fp1);
fclose(fp2);
fclose(fp3);

fclose(fp4);
fclose(fp5);
fclose(fp6);

注意: 只能对已打开的文件进行关闭,若文件不存在,会报错

2.3文件写入

 C语言 对于文件写入有这几种方式:fputcfputsfwritefprintf 和 snprintf

 

int fputc ( int character, FILE * stream );

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

size_t fwrite ( const void * ptr, size_t size, size_t count, FILE * stream );

int snprintf ( char * s, size_t n, const char * format, ... );

前几种方式比较简单,无非就是 逐字符写入、逐行写入 与 格式化写入,这里主要来介绍一下 snprintf

snprintf 是 sprintf 的优化版,增加了读取字符长度控制,更加安全

参数1:缓冲区,常写做 buffer 数组
参数2:缓冲区的大小
参数3:格式化输入,比如 "%d\n", 10

使用 snprintf 函数写入数据至缓冲区后,可以再次通过 fputs 函数,将缓冲区中的数据真正写入文件中

 

#include <stdio.h>
#include <stdlib.h>

#define LOG "log.txt" //日志文件
#define SIZE 32

int main()
{
  FILE* fp = fopen(LOG, "w");
  if(!fp)
  {
    perror("fopen file fail!"); //报错
    exit(-1); //终止进程
  }

  char buffer[SIZE];  //缓冲区
  int cnt = 5;
  while(cnt--)
  {
    snprintf(buffer, SIZE, "%s\n", "Hello File!");  //写入数据至缓冲区
    fputs(buffer, fp);  //将缓冲区中的内容写入文件中
  }

  fclose(fp);
  fp = NULL;
  return 0;
}

 可以灵活地向日志文件中写入内容

2.4文件读取

读取与写入配套出现

int fgetc ( FILE * stream );

char * fgets ( char * str, int num, FILE * stream );

size_t fread ( void * ptr, size_t size, size_t count, FILE * stream );

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

int sscanf ( const char * s, const char * format, ...);

 可以使用 sscanf 按照一定的规则格式化读取字符串 s

#include <stdio.h>

int main()
{
  char s[] = "2023:3:24";
  int arr[3];
  char* buffer[4];
  sscanf(s, "%d:%d:%d", arr, arr + 1, arr + 2);
  printf("%d\n%d\n%d\n", arr[0], arr[1], arr[2]);

  return 0;
}

 

这个函数多用于 序列化与反序列化操作 


3.系统文件操作 

3.1open 

手册指令:

man 2 open//2 表示系统手册

 

#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);
int creat(const char *pathname, mode_t mode);

pathname表示需要打开文件的路径,mode表示设置的权限,flags表示标志位,对于这个标志位,它的实现思路为:

一个整形有32个bit位,那么可以表示32种标志,但是这样不方便,因为要传递该标志位对应的整数,比如要表示(……0000 1000)就要传递8,这样其实不太方便,所以采用位操作的方法;

这种方法就是在函数内定义宏,通过位运算来查找到对应的指令操作是什么(例如read,write等);

可以通过下图理解:

 

助记demo:

#include <stdio.h>
#include <stdlib.h>

#define ONE 0x1
#define TWO 0x2
#define THREE 0x4

void Test(int flags)
{
  //模拟实现三种选项传递
  if(flags & ONE)
    printf("This is one\n");

  if(flags & TWO)
    printf("This is two\n");

  if(flags & THREE)
    printf("This is three\n");
}

int main()
{
  Test(ONE | TWO | THREE);
  printf("-----------------------------------\n");
  Test(ONE);  //位图使得选项传递更加灵活
  return 0;
}

函数 open 中的参数2正是位图,其参数有很多个,这里列举部分

  O_RDONLY    //只读
 O_WRONLY    //只写
 O_APPEND    //追加
 O_CREAT    //新建
 O_TRUNC    //清空

实际使用时,可以按照位图demo中的方式进行参数传递

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h> //write 的头文件


#define LOG "log.txt" //日志文件
#define SIZE 32

int main()
{
  //三种参数组合,就构成了 fopen 中的 w
  int fd = open(LOG, O_WRONLY | O_CREAT | O_TRUNC, 0666);	//权限最好设置
  if(fd == -1)
  {
    perror("open file fail1");
    exit(-1);
  }

  const char* ps = "Hello System Call!\n";
  int cnt = 5;
  while(cnt--)
    write(fd, ps, strlen(ps));  //不能将 '\0' 写入文件中

  close(fd);
  return 0;
}

 

注意:

假若文件不存在,open 中的参数3最好进行设置,否则创建出来的文件权限为随机值
继承环境变量表后,umask 默认为 0002,当然也可以自定义
通过系统级函数 write 写入字符串时,不要刻意加上 '\0',因为对于系统来说,这也只是一个普通的字符('\0' 作为字符串结尾只是 C语言 的规定)
C语言 中的 fopen 调用 open 函数,其中的选项对应关系如下

  • w -> O_WRONLY | O_CREAT | O_TRUN
  • a -> O_WRONLY | O_CREAT | O_APPEND
  • r -> O_RDONLY

所以只要我们想,使用 open 时,也能做到 只读方式 打开 不存在的文件,也不会报错,加个 O_CREAT 参数即可

3.2close

 close 函数根据文件描述符关闭文件

#include <unistd.h>

int close(int fildes);

Linux 下一切皆文件

包括这三个标准流: stdinstdoutstderr
它们的文件描述符依次为:012,也可以通过 close(1) 的方式,关闭标准流

3.3write

write 函数的返回值类型有点特殊,但使用方法与 fwrite 基本一致

#include <unistd.h>

ssize_t write(int fildes, const void *buf, size_t nbyte);

 3.4read

 read 读取很淳朴,只支持指定字符数读取

#include <unistd.h>

ssize_t read(int fildes, void *buf, size_t nbyte);

文件读取时,同样是借助缓冲区进行读取

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h> //write 的头文件


#define LOG "log.txt" //日志文件
#define SIZE 1024

int main()
{
  int fd = open("test.c", O_RDONLY);
  if(fd == -1)
  {
    perror("open file fail1");
    exit(-1);
  }

  int n = 50; //读取50个字符
  char buffer[SIZE];
  int pos = 0;
  while(n--)
  {
    read(fd, (char*)buffer + pos, 1);
    pos++;
  }

  printf("%s\n", buffer);

  close(fd);
  return 0;
}

 4.访问文件本质

struct file:每打开一个文件,OS都会分配一个file对象给到对应文件,里面保存了文件相关的属性信息

struct file_struct:该结构体包含一个指针数组,该数组类型为struct file* fd_array[32];这个指针数组的作用就是将对应文件描述符的地址写到该数组对应下标中(这个过程相当于分配下标),例如默认打开的标准输入、标准输出、标准错误分别对应下标0、1、2;

struct file_struct* file:这是一个指针,指向上述的指针数组,其作用是让进程拿到上述操作后,对应文件的描述符fd。 

 通过上面的分析,也可以了解到为什么write、read等系统调用函数都需要文件描述符fd,因为拿到了fd,才能找到并对对应文件进行操作。

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

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

相关文章

职业性格在求职应聘和跳槽中的作用

性格测试对跳槽者的影响大不大&#xff1f;首先我们要弄清楚两个问题&#xff0c;性格对我们的职业生涯又没有影响&#xff0c;性格测试是什么&#xff0c;职场中有哪些应用&#xff1f;性格可以说从生下来就有了&#xff0c;随着我们的成长&#xff0c;我们的性格也越来越根深…

Stable Diffusion教程——常用插件安装与测试(一)

前言 随着Stable Diffusion不断演进&#xff0c;越来越多的开发者开始涉足插件开发。尽管网络上存在大量教程&#xff0c;但它们通常零散分布&#xff0c;逐个学习和查找非常耗时&#xff0c;使人感觉每天都在劳累思考。这里总结了Stable Diffusion常用的插件安装与测试方法。…

【JavaScript】点击选中和取消

效果图 &#xff08;1&#xff09;选中 &#xff08;2&#xff09;取消选中 实现 <template><div class"qualitityIssues"><div style"display: flex;"><div class"course-area"><div :class"checkoutIndex …

element table 点击按钮 表格滚动条移动

需求场景&#xff1a;表格列数很多的情况下&#xff0c;不想拖拽滚动条查看明细&#xff0c;所以点击按钮直接跳转到对应的位置 代码实现&#xff1a; <template><div><div class "mytable"><el-tableid "testTable"ref "t…

Qt之条件变量QWaitCondition详解(从使用到原理分析全)

QWaitCondition内部实现结构图&#xff1a; 相关系列文章 C之Pimpl惯用法 目录 1.简介 2.示例 2.1.全局配置 2.2.生产者Producer 2.3.消费者Consumer 2.4.测试例子 3.原理分析 3.1.源码介绍 3.2.辅助函数CreateEvent 3.3.辅助函数WaitForSingleObject 3.4.QWaitCo…

计算机网络之网络安全

文章目录 1. 网络安全概述1.1 安全威胁1.1.1 被动攻击1.1.2 主动攻击 1.2 安全服务 2. 密码学与保密性2.1 密码学相关基本概念2.2 对称密钥密码体制2.2.1 DES的加密方法2.2.2.三重DES 2.3 公钥密码体制 3. 报文完整性与鉴别3.1 报文摘要和报文鉴别码3.1.1 报文摘要和报文鉴别码…

基于微信小程序的健身房私教预约系统,附源码

博主介绍&#xff1a;✌程序员徐师兄、7年大厂程序员经历。全网粉丝12w、csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f447;…

模型可视化

模型标题可视化 可视化工具&#xff1a;Graphiz、Netron、ZetaneEngine 7.2.2Graphiz可视化工具 Graphiz是一个由AT&T实验室启动的开源工具包&#xff0c;用于绘制DOT语言脚本描述的图形&#xff0c;使用它可以非常方便地对任何图形进行可视化。 Graphiz的使用步骤包括创…

【ChatIE】论文解读:Zero-Shot Information Extraction via Chatting with ChatGPT

文章目录 介绍ChatIEEntity-Relation Triple Extration (RE)Named Entity Recognition (NER)Event Extraction (EE) 实验结果结论 论文&#xff1a;Zero-Shot Information Extraction via Chatting with ChatGPT 作者&#xff1a;Xiang Wei, Xingyu Cui, Ning Cheng, Xiaobin W…

PyTorch使用Tricks:学习率衰减 !!

文章目录 前言 1、指数衰减 2、固定步长衰减 3、多步长衰减 4、余弦退火衰减 5、自适应学习率衰减 6、自定义函数实现学习率调整&#xff1a;不同层不同的学习率 前言 在训练神经网络时&#xff0c;如果学习率过大&#xff0c;优化算法可能会在最优解附近震荡而无法收敛&#x…

算法刷题:长度最小的子数组

长度最小的子数组 .题目链接题目详情算法原理滑动窗口定义指针进窗口判断出窗口 我的答案 . 题目链接 长度最小的子数组 题目详情 算法原理 滑动窗口 这道题,我们采用滑动窗口的思想来解决,具体步骤如图所示 定义指针 如图所示,两个指针都需要从左往右进行遍历,因此初始值…

AIGC实战——能量模型(Energy-Based Model)

AIGC实战——能量模型 0. 前言1. 能量模型1.1 模型原理1.2 MNIST 数据集1.3 能量函数 2. 使用 Langevin 动力学进行采样2.1 随机梯度 Langevin 动力学2.2 实现 Langevin 采样函数 3. 利用对比散度训练小结系列链接 0. 前言 能量模型 (Energy-based Model, EBM) 是一类常见的生…

食物厨艺展示404错误页面模板源码

食物厨艺展示404错误页面模板源码&#xff0c;HTMLCSSJSCSS,记事本打开源码文件可以进行内容文字之类的修改&#xff0c;双击html文件可以本地运行效果&#xff0c;也可以上传到服务器里面&#xff0c;重定向这个界面 蓝奏云&#xff1a;https://wfr.lanzout.com/i3uC71oj52ah…

初识数据库:探索数据的世界

初识数据库&#xff1a;探索数据的世界 1. 什么是数据库&#xff1f;2. 数据库的类型2.1 关系型数据库&#xff08;RDBMS&#xff09;2.2 非关系型数据库&#xff08;NoSQL&#xff09; 3. 为什么使用数据库&#xff1f;4. 如何选择合适的数据库&#xff1f;5. 结语 在信息技术…

ovs和ovn安装

ovn和ovs介绍 ovn架构图 CMS||-----------|-----------| | || OVN/CMS Plugin || | || | || OVN Northbound DB || | || | || ovn-northd || …

设置windows10资源管理器等的边框

Windows10默认状态下&#xff0c;资源管理器、浏览器等没有边框&#xff0c;在打开多个窗口等情况下&#xff0c;想要拖动或选择某个窗口时&#xff0c;不是很好定位到窗口标题栏。 通过&#xff1a;设置&#xff08;可以通过wini组合键打开设置&#xff09;---》个性化 ---》…

第十二章[模块]:12.4:标准库:datetime

一,官方文档: 1,文档地址 datetime --- 基本日期和时间类型 — Python 3.12.2 文档源代码: Lib/datetime.py datetime 模块提供了用于操作日期和时间的类。 在支持日期时间数学运算的同时,实现的关注点更着重于如何能够更有效地解析其属性用于格式化输出和数据操作。 感知型…

vue-自定义创建项目(六)

为什么要自定义创建项目&#xff1f; 因为VueCli默认创建的项目不能够满足我们的要求&#xff0c;比如默认的项目中没有帮我们集成路由&#xff0c;vuex&#xff0c;eslink等功能。 默认项目 自定义创建项目 流程&#xff1a; 创建项目命令&#xff1a;vue create custom_dem…

VNCTF 2024 Web方向 WP

Checkin 题目描述&#xff1a;Welcome to VNCTF 2024~ long time no see. 开题&#xff0c;是前端小游戏 源码里面发现一个16进制编码字符串 解码后是flag CutePath 题目描述&#xff1a;源自一次现实渗透 开题 当前页面没啥好看的&#xff0c;先爆破密码登录试试。爆破无果…

项目第一次git commit后如何撤销

问题描述&#xff1a; # 1. 新建gitcode目录&#xff0c;然后在目录下 git init# 2. 用idea打开目录后&#xff0c;新建.gitignore文件后 git add .git commit -m "init project"git log# 3. 就出现如下图情况目的&#xff1a;向撤销该次代码提交 # 仅撤销 git com…