当前位置: 首页 > news >正文

Linux系统程序设计:从入门到高级Day03

知识点3【文件IO的操作】

上篇文章中我 帮助大家回忆了C语言中的文件的概述及其操作函数

现在我们来学习一下 Linux中的文件的操作

这里说一下 我下面介绍 都是从头文件 函数(函数功能 参数 返回值)进行介绍,在介绍之后我会举例让大家更好地理解和使用。

常见的文件操作API:open close read write 下面我们依次介绍一下

打开文件 open

由于这里是第一个介绍的,我将教大家这么查询

在Linux 终端 中输入指令  man 2 open

头文件

#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)

下面我们分别称为二参open和三参open

函数功能

打开文件,如果文件不存在则可以选择创建

二参open和三参open有什么区别呢?

二参open:

用来打开已经存在的文件

三参open:

用来打开不存在的文件,当文件不存在时,并加以创建文件的行为标志,mode会指定新建文件的权限(所有者权限,同组用户权限,其他用户权限)

问题:这里是选择性创建,在哪里选择呢?

函数参数

pathname

        打开文件的路径及文件名

flags

        打开文件的行为标志,我带大家在man 2 open中查找

(大家一定要学会查找,Linux中的API背根本背不完)

我来简述一下:

第一段中 加粗的是必选项

第二段中 加粗的是可选项

详细介绍:

必选项

可选项

可选项和必选项 需要按位或(|)起来

上面的问题,选择性创建 就是由行为标志flags 决定的

补充一下flags的使用技巧

在我们写入操作时,常这样写:fd_w = open(“00_shell.sh”,O_WRONLY | O_CREAT,0775);

在我们读操作时, 常这样写:fd_r = open(“01_shell.sh”,O_RDONLY)

因为写入时文件不存在我们需要创建一个新文件

但是读的时候,我们无需创建系文件 因为读取空文件也没有意义

mode

这个我们在Day01 中有介绍这里仅进行简述

mode用八进制表示的

表示都是 0xxx  每个x都是421组合 4:r   2:w   1:x

这三个x的代表请看下图

例如 0775  就是 所有者可读可写可执行  同组用户可读可写可执行  其他用户只读不可写可执行

补充:系统的掩码

功能介绍

系统默认会屏蔽的功能

函数返回值

成功:返回打开的文件描述符

失败:-1

掩码

引入

我们设置的是 0777权限,但是实际的权限确实0775

这就是掩码在起作用

默认屏蔽掉 访客的写权限

查看掩码

umask

掩码底层实现

给定权限 & (~umask)

设置掩码

umask mode

知道就行 不要实操!不要实操!不要实操!

查看各组用户的默认操作权限

umask -S

代码演示:
#include <stdio.h>

#include <sys/types.h>

#include <sys/stat.h>

#include <fcntl.h>

#include <unistd.h>

int main(int argc,const char *argv[])

{

    int fd;

    fd = open("text.txt",O_WRONLY | O_CREAT,0777);

    if(fd < 0)

    {

        perror("open");

        return 0;

    }

    printf("%d\n",fd);

    while(1)

    {

    }

    close(fd);

    return 0;

}
代码运行结果

注意 

一定要在代码运行器件 去查看文件的进程号(原因我们将在进程的课程中讲解)

关闭文件 close

头文件

#include <unistd.h>

函数

int close(int fd);

函数功能

关闭已打开的文件

函数参数

fd:文件描述符,open()的返回值

函数返回值

成功:0

失败:-1

注意:

close工作步骤,先将文件描述符的个数减一,如果文件描述符的个数为0,才会回收文件描述符所占用的内核空间

解释:当我们有两个代码(不是进程),A B,这两个文件都打开同一个文件,那么他们得到的文件描述符是相同的,如果A执行close,这时仅会将文件描述符的个数减一,并不会将文件关闭,直到所有打开 该文件 的代码全部关闭该文件即文件描述符个数为0时后,才会真正意义上的关闭该文件(释放该文件)。

向文件写数据 write

头文件

#include <unistd.h>

函数功能

将指定数目的数据写到文件中

函数

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

函数参数

fd:文件描述符

buf:要写入数据的首地址

count:写入数据的最大长度(字节数)

函数返回值

成功:实际写入的数据个数

失败:-1

代码演示

#include <stdio.h>

#include <string.h>

#include <sys/types.h>

#include <sys/stat.h>

#include <fcntl.h>

#include <unistd.h>

int main(int argc,const char *argv[])

{

    int fd_w;

    fd_w = open("text.txt",O_WRONLY | O_CREAT,0777);

    if(fd_w < 0)

    {

        perror("open");

        return 0;

    }



    //先创建一个数组 并从键盘获取数据数据

    char buf[128] = "";

    fgets(buf,sizeof(buf),stdin);

    buf[strlen(buf) - 1] = '\0';//这里注意 '0'和'\0'是不一样的

    //将数组的数据写入到磁盘文件中

    write(fd_w,buf,sizeof(buf));



    //关闭文件

    close(fd_w);

    return 0;

}
代码运行结果

补充:

如果不关闭文件的后果:

  1. 文件打开时间过长的时候,会造成系统资源的浪费
  2. 内核会进行页面刷新,类似于用户态的缓冲区。文件的页存满了会进行页刷新,但是如果不关闭文件,最后会有数据残留在页面缓存部分,导致数据丢失。

从文件读数据 read

头文件

#inclue <unistd.h>

函数功能

头文件个数的数据读取到文件当中

函数

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

函数参数

fd:文件描述符

buf:内存首地址

count:最多读取的字节个数

函数返回值

成功:返回读取到的字节个数

失败:-1

代码演示

#include <stdio.h>

#include <string.h>

#include <sys/types.h>

#include <sys/stat.h>

#include <fcntl.h>

#include <unistd.h>

int main(int argc,const char *argv[])

{

    int fd_w;

    fd_w = open("text.txt",O_RDWR | O_CREAT,0777);

    if(fd_w < 0)

    {

        perror("open");

        return 0;

    }



    //先创建一个数组 并从键盘获取数据数据

    printf("fd_w的文件描述符:%d\n",fd_w);

    char buf[128] = "";

    printf("请输入:");

    fgets(buf,sizeof(buf),stdin);

    buf[strlen(buf) - 1] = '\0';//这里注意 '0'和'\0'是不一样的

    //将数组的数据写入到磁盘文件中

    write(fd_w,buf,sizeof(buf));



    //读取数据

    char buf1[128] = "";

    int num1 = 0;

    num1 = read(fd_w,buf1,sizeof(buf1));

    printf("buf1 = %s\n",buf1);

   

    //关闭文件

    close(fd_w);

    return 0;

}
代码运行结果

代码分析

这次代码我实在 write函数的基础上写的,只添加了读取数据的部分,和修改和行为标志(O_RDWR)。

这里我想解释的是,这里buf1中数据为空,实际和C语言中文件操作是一样的,需要一个偏移量复位的过程,在C中是流指针复位(如果感到陌生的请看我的Day2 的内容)

如果要buf1成功读取数据 我这里提供两个方法

方法1

先关闭fd_w,然后后以 只读 的行为标志打开fd_r(实现复位操作)

方法2

使用lseek函数

lseek

函数功能

调整文件描述符的读写位置

注意:它的功能是(类比记忆)fseek,ftell,rewind(这里不可以使用)的结合

函数

off_t lseek(int fd, off_t offset, int whence);

函数参数

fd:文件描述符

offset:偏移量(可正可负)  这个偏移量是以whence为基准的偏移量

whence:枚举

SEEK_SET:文件开头。

SEEK_CUR:当前偏移量位置。

SEEK_END:文件末尾。

函数返回值

返回新的文件偏移量(距离文件开头)

代码演示

    //符文文件偏移量

    lseek(fd_w,0,SEEK_SET);

将上面这部分 加上到读取数据的前面即可

代码运行结果:

主要的文件IO操作就已经讲完了

现在我们来写一个小作业 来给今天的内容进行一个总复习吧。

综合题目练习

题目:代码实现linux中的cp执行 cp 目标文件 目标目录

在写之前,我们先来补充一个知识点:

main函数的参数

int main(int argc,const char *argv[])

{

}

相信大家都经常写这个函数,那大家知道参数argc和argv的含义吗?

argc:命令行参数的个数

argv:指针数组类型,存储所有命令行参数的字符串值

他们两个是是程序与命令行交互的桥梁

例子说明

./demo hello world 123

argc = 4

argv[0]:./demo

argv[1]:hello

argv[2]:world

argv[3]:123

argv[argc]:NULL

argv[argc] 之后是没有定义的

好了知识点补充完了让我们开始题目吧

代码

代码思路

  1. 参数个数限制
  2. 参数的提取及处理

处理是指路径和目标文件名的拼接操作

  1. 读文件
  2. 写文件

代码展示

#include <stdio.h>

#include <sys/types.h>

#include <sys/stat.h>

#include <fcntl.h>

#include <unistd.h>



int main(int argc,const char *argv[])

{

    //指令参数个数判断

    if(argc != 3)

    {

        printf("参数数目错误\n");

        return 0;

    }



    //参数的提取和处理

    char path[128] = "";

    sprintf(path,"%s/%s",argv[2],argv[1]);



    //打开文件

    int fd_r = open(argv[1],O_RDONLY);

    if(fd_r < 0)

    {

        perror("读文件失败");

        return 0;

    }

    int fd_w = open(path,O_WRONLY | O_CREAT,0775);

    if(fd_w < 0)

    {

        perror("写文件失败");

        return 0;

    }



    //文件读取

    int size = 0;

    while(1)

    {

        char arr[128] = "";

        size = read(fd_r,arr,sizeof(arr));

        write(fd_w,arr,sizeof(arr));

        if(size < sizeof(arr))

        {

            break;

        }

    }



    //关闭文件

    close(fd_r);

    close(fd_w);



    return 0;

}

代码运行结果

结束

代码重在练习,只是脑子懂是远远不够的,跟着博客一起手敲,会有更多收获。

今天的内容就到此结束了,希望对你能够有所帮助,如果你喜欢我的分享,请点赞收藏加关注,谢谢大家!!!

http://www.dtcms.com/a/113550.html

相关文章:

  • 第八章:流量治理_《凤凰架构:构建可靠的大型分布式系统》
  • DDPM 做了什么
  • 2007-2019年各省地方财政其他支出数据
  • 格式工厂怎样插入内置音频文件
  • 硬件工程师面试问题(五):蓝牙面试问题与详解
  • 在响应式网页的开发中使用固定布局、流式布局、弹性布局哪种更好
  • vllm作为服务启动,无需额外编写sh文件,一步到位【Ubuntu】
  • 『Linux_网络』 第一章 网络基础概念
  • 分表字段选择策略:以电商交易订单为例的最佳实践
  • Java项目之基于ssm的怀旧唱片售卖系统(源码+文档)
  • 大数据时代的隐私保护:区块链技术的创新应用
  • 通过构造函数和几何条件,研究了不同函数的最近点存在性、性质及单调性
  • ZKmall开源商城多云高可用架构方案:AWS/Azure/阿里云全栈实践
  • 紧急更新!MinIO发布RELEASE.2025-04-03T14-56-28Z版本,修复高危漏洞CVE-2025-31489,用户需立即升级!
  • raft协议中一条数据写入流程
  • Java 实现插入排序:[通俗易懂的排序算法系列之三]
  • 文献总结:TPAMI综述BEV感知—Delving into the devils of bird‘s-eye-view perception
  • Socket编程TCP
  • HarmonyOS:WebView 控制及 H5 原生交互实现
  • 硬件学习之器件篇-蜂鸣器
  • 第三章 react redux的学习之redux和react-redux,@reduxjs/toolkit依赖结合使用
  • use_tempaddr 笔记250405
  • setj集合
  • 1.5 基于改进蛇优化VGG13SE故障诊断方法的有效性分析
  • Python实现链接KS3,并将文件数据上传到KS3
  • 【spring Cloud Netflix】OpenFeign组件
  • 第二十九章:Python-mahotas库:图像处理的高效工具
  • 使用 pytest-xdist 进行高效并行自化测试
  • PHP的垃圾回收机制
  • 我的创作历程:从不情愿到主动分享的成长