【Linux】—— 在Linux上进行读写文件操作

2023-09-13 10:17:44

前言:

  • 在之前,我已经对进程的相关知识进行了详细的介绍。本期开始,我们将要学习的是关于 “基础I/O”的知识!!!

目录

(一)C文件接口

 (二)系统文件I/O

1、接口介绍

2、代码示例

(三)总结


(一)C文件接口

首先,在正式进入本期主题之前,我先用C文件的接口带大家简单的回顾下,顺便带大家认识相关的接口函数等。

首先就是往文件里面进行写数据操作:

#include <stdio.h>
#include <string.h>

#define LOG "log.txt"

int main()
{
    FILE *fp = fopen(LOG , "w");

    if(!fp)
    {
        perror("fopen");
        return 1;
    }

    const char *msg = "hello bit!\n";
    int count = 5;
    while(count--)
    {
        //fputs(msg,fp);
        fwrite(msg, strlen(msg), 1, fp);
    }

    fclose(fp);
    return 0;
}

输出展示:

【解释说明】

  1. 上述代码简单的实现了打开文件、写入文本内容,并关闭文件。在系统中,提供了丰富的用于对文件进行操作的函数;
  2. 打开文件使用 fopen 函数以写入模式打开文件,返回一个文件指针 fp;
  3. 如果文件打开失败,perror 函数将输出错误信息到标准错误输出流。
  4. 其次使用fwrite函数将字符串msg通过文件指针fp写入文件(除了这个外还有很多)

首先就是读文件里面的数据操作:

#define LOG "log.txt"
 
int main()
{
  
   FILE *fp = fopen(LOG, "r");
   if(!fp)
   {
     perror("fopen");
     return -1;
    }
    //正常进行文件操作
    while(1)
    {
      char line[128];
      if(fgets(line, sizeof(line), fp) == NULL) break;
      else printf("%s", line);
    }

    fclose(fp);
    return 0;
}

输出展示:

【解释说明】

  • 同样的对于读文件操作,在系统内部也提供的很多的函数去进行实现!

如上,是我们之前学的文件相关操作。还有 fseek ftell rewind 的函数。


 (二)系统文件I/O

操作文件,除了上述C接口(当然,C++也有接口,其他语言也有),我们还可以采用系统接口来进行文件访问.

先来直接以代码的形式,实现和上面一模一样的代码(写操作):

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>

int main()
{
    umask(0);
    int fd = open("myfile", O_WRONLY|O_CREAT, 0644);
    if(fd < 0){
        perror("open");
    return 1;
    }
    int count = 5;
    const char *msg = "hello bit!\n";
    int len = strlen(msg);
    while(count--){
        write(fd, msg, len);
        //fd: 后面讲, msg:缓冲区首地址, len: 本次读取,期望写入多少个字节的数据。 
        //返回值:实际写了多少字节数据
    }
    close(fd);
    return 0;
}
  • C++中对文件进行读操作可写成下述:
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>

int main()
{
    int fd = open("myfile", O_RDONLY);
    if(fd < 0){
        perror("open");
        return 1;
    }
    const char *msg = "hello bit!\n";
    char buf[1024];
    while(1){
        ssize_t s = read(fd, buf, strlen(msg));//类比write
        if(s > 0){
            printf("%s", buf);
        }
        else{
            break;
         }
    }
    close(fd);
    return 0;
}

1、接口介绍

接下来,我首先带大家认识相关的接口。随后大家在来理解上述这两段代码!!

  • 最重要的当然是 open() ,通过对它的学习,我们将极大程度的理解有些知识

【解释说明】

参数

  • path:要打开的文件的路径字符串。
  • flags:打开文件的标志,可以使用一或多个以下标志的位或运算符 |

  第一个参数很好理解,对于第二个参数大家可能存在些疑惑我在这里重点讲讲关于这个标志的问题。

💨 首先,引入一个问题:OS是如何让用户给自己传递标志位的呢?

接下来,我给大家写个简单的 demo 样例帮助大家理解上述逻辑:

#include <stdio.h>

#define ONE 0x1
#define TWO 0x2
#define THREE 0x4
#define FOUR 0x8
#define FIVE 0x10

// 0000 0000 0000 0000 0000 0000 0000 0000
void Print(int flags)
{
    if(flags & ONE) printf("hello 1\n"); //充当不同的行为
    if(flags & TWO) printf("hello 2\n");
    if(flags & THREE) printf("hello 3\n");
    if(flags & FOUR) printf("hello 4\n");
    if(flags & FIVE) printf("hello 5\n");
}


int main()
{
    printf("--------------------------\n");
    Print(ONE);
    printf("--------------------------\n");
    Print(TWO);
    printf("--------------------------\n");
    Print(FOUR);
    printf("--------------------------\n");

    Print(ONE|TWO);
    printf("--------------------------\n");

    Print(ONE|TWO|THREE);
    printf("--------------------------\n");

    Print(ONE|TWO|THREE|FOUR|FIVE);
    printf("--------------------------\n");

    return 0;
}

输出展示:

 【解释说明】

  1. 上述代码展示了使用位运算来模拟标志位。在这个示例中,每个标志位都表示一种不同的行为,当相应的位被设置时,相关的行为会执行;
  2. 首先,定义了一组宏来表示不同的标志位,每个标志位都对应一个唯一的二进制位。例如,ONE的二进制表示是:0000 0000 0000 0000 0000 0000 0000 0001TWO的二进制表示是:0000 0000 0000 0000 0000 0000 0000 0010,依此类推。
  3. 接下来,定义了一个Print函数,它接受一个整数参数flags,这个参数表示一组标志位的组合。在函数内部,使用位运算和按位与操作符&来检查每个标志位是否被设置。

有了上述认识,大家在返回去看 open() 函数中的 flags参数,我想就很明显了。当我们在man手册中往下面翻的时候就会看到如下内容:

  • 这一个个的选项就相当于宏,我们只需关于宏函数内部的意义即可!

2、代码示例

接下来,我们通过写代码的方式来进行理解!!

首先,有如下代码示例:

int main()
{
   int fd = open(LOG,O_WRONLY);
   if(fd == -1)
   {
      printf("fd: %d, errno: %d, errstring: %s\n", fd, errno, strerror(errno));
   }
   else printf("fd: %d, errno: %d, errstring: %s\n", fd, errno, strerror(errno));
 
   close(fd);
   return 0;
 }
 

输出展示:

【解释说明】

  1. 上述代码的主要目的是打开一个文件并检查是否成功打开。如果打开失败,它将输出打开文件描述符(fd)、错误代码(errno)和错误字符串(strerror(errno));
  2. 然而,需要注意的是,因为开始并没有提供LOG这个文件,因此最后的输出肯定是发生报错。

解决方法就是在我们 open 的时候,再加上 【o_CREAT】这个标志:

输出展示:

然而,此时出现了一个奇怪的现象,我们通过查看得知 log.txt 文件的权限是处于乱码的情况:


因此,基于上述情况的出现。我们一般在用的时候一般不用 带两个参数的 open() ,而是用带三个参数的open() 函数

【解释说明】

  1. mode 是一个mode_t类型的参数,用于设置文件的权限;
  2. 它指定了文件的访问权限位,并且只有在使用O_CREAT标志创建文件时才会生效;
  3. 通常使用8进制表示权限位。例如,0644表示文件所有者具有读写权限,其他用户只有读权限。

基于上述情况,我们在后面加上相应的权限(我这里加的是0666):

输出展示: 

【解释说明】

  • 请注意,文件权限可能会受到操作系统的默认配置或其他因素的影响,即 umask;

  •  此时,系统默认的 umask 为2,因此在某些情况下,系统可能会对文件权限进行适当的修改,以使其符合操作系统的权限策略。

那么要如何保障这里的权限不受系统的影响呢?—— 我们可以自己设置

  • 此时,我们又需要引入一个新的函数:

接下来,我们对代码进行修改操作: 

输出展示:

【注意】

  • open 函数具体使用哪个,和具体应用场景相关,如目标文件不存在,需要open创建,则第三个参数表示创建文件的默认权限,否则,使用两个参数的open。

接下来,我们就需要往文件中写入数据了。在C++中,系统提供 write() 函数来进行写操作。

代码展示:

输出展示:


上述完成写入数据,接下来就是从文件中读出数据。在C++中,系统提供了 read() 函数

man手册查询:

代码展示:

输出展示:

【小结】

  • 有了上述这些认识,在回过头去看我给出的 读和写 的代码就容易了!

(三)总结

上面的 fopen fclose fread fwrite 都是C标准库当中的函数,我们称之为库函数(libc);


open close read write lseek 都属于系统提供的接口,称之为系统调用接口


回忆一下我们讲操作系统概念时,画的一张图:

  1. 根据上述图片,我们可以知道系统调用接口和库函数的关系,一目了然。
  2. 所以,可以认为,f#系列的函数,都是对系统调用的封装,方便二次开发。

更多推荐

基于Java个性化美食推荐系统设计实现(源码+lw+部署文档+讲解等)

博主介绍:✌全网粉丝30W+,csdn特邀作者、博客专家、CSDN新星计划导师、Java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌🍅文末获取源码联系🍅👇🏻精彩专栏推荐订阅👇🏻不然下次找不到哟2022-2024年最全的计算机软件毕业设计选题

基于香橙派和SU-03T 使用Linux实现语音控制刷抖音

硬件介绍SU-03T之前在小车的时候使用过,详见:语音小车---6+最终整合_mjmmm的博客-CSDN博客按照下图进行接线:项目需求通过语音指令来控制安卓手机刷抖音,可以实现视频切换和点赞等功能:1.开机播报“你好,我是你的刷抖音助手”1.当说出“你好抖音助手"可以唤醒模块,模块回复“抖音助手在”2.当超过10s没有

七天学会C语言-第一天(C语言基本语句)

一、固定格式这个是C程序的基本框架,需要记住!!!#include<stdio.h>intmain(){return0;}二、printf语句简单输出一句C程序:#include<stdio.h>intmain(){printf("大家好,");printf("我是");printf("沐尘而生!");return0;

9月15日、9月18日上课内容 Zookeeper集群 + Kafka集群

Zookeeper本章结构Zookeeper概述Zookeeper定义*(了解)Zookeeper是一个开源的分布式的,为分布式框架提供协调服务的Apache项目。Zookeeper工作机制*****(非常重要,需要掌握)Zookeeper从设计模式角度来理解:是一个基于观察者模式设计的分布式服务管理框架,它负责存储和

【网络编程】TCP Socket编程

TCPSocket编程1.ServerSocket2.Socket3.TCP的长短连接4.Socket通信模型5.代码示例:TCP回显服务器流套接字:使用传输层TCP协议TCP:即TransmissionControlProtocol(传输控制协议),传输层协议。TCP的特点:有连接可靠传输面向字节流有接收缓冲区,也有

Linux下运行Jmeter压测

一、在Linux服务器先安装SDK1、先从官网下载jdk1.8.0_131.tar.gz,再从本地上传到Linux服务器2、解压:tar-xzfjdk1.8.0_131.tar.gz,生成文件夹jdk1.8.0_1313、在/usr/目录下创建java文件夹,再将jdk1.8.0_131目录移动到/usr/java中1

【Linux从入门到精通】多线程 | 线程互斥(互斥锁)

上篇文章我们对线程|线程介绍&线程控制介绍后,本篇文章将会对多线程中的线程互斥与互斥锁的概念进行详解。同时结合实际例子解释了可重入与不被重入函数、临界资源与临界区和原子性的概念。希望本篇文章会对你有所帮助。文章目录引入一、重入与临界1、1可重入与不被重入函数1、1、1不可重入函数1、1、2可重入函数1、2临界资源与临界

手撕单链表

>作者简介:დ旧言~,目前大一,现在学习Java,c,c++,Python等>座右铭:松树千年终是朽,槿花一日自为荣。>望小伙伴们点赞👍收藏✨加关注哟💕💕🌟前言前面我们已经学习了顺序表,顺序表可以存储动态的数据,但是一旦元素过少,而又要开辟空间,这样就造成空间的浪费,为了解决这类问题,人们发现了单链表,把一个一

QQ邮箱怎么设置SMTP接口服务器?

在现如今信息快速传递的时代,邮件已成为我们工作、学习和生活中必不可少的一部分。而作为每位用户必备的一款邮箱,QQ邮箱一直以其稳定、高效、安全的特点深受大家的青睐。但是你是否觉得每次发邮件都需要打开QQ邮箱网页,进行繁琐的操作很是麻烦呢?其实,QQ邮箱也提供了SMTP接口服务器,使得我们可以直接调用SMTP接口,让发信更

如何通过bat批处理实现快速生成文件目录,一键生成文件名和文件夹名目录

碰对了情人,相思一辈子。具体方法步骤:一、创建一个执行bat文件(使用记事本即可);1、新建一个txt文本空白记事本文件2、复制以下内容进记事本内dir/a/s/b>LIST.TXT(其中LIST.TXT文件名是提取后将要自动新建的文本文件)二、记事本保存,文件名可以任意写三、把保存的文件名后缀.txt改为.bat,这

网络路径监控分析

不间断的连接应该是任何企业的首要任务。然而,确保网络中的源和目标之间持续、不间断的联系一直是网络通信中一个劳动密集型的过程。了解网络路径中的障碍、识别它们并迅速解决它们以维护健康、不间断的网络至关重要。为什么要监控网络路径维护网络运行状况是任何LAN或WAN网络中最重要的因素。在数据传输期间,无法查明和排查网络路径中跃

热文推荐