Linux C编程:文件IO(概念、打开、读、写、关闭)

ops/2025/1/17 17:40:25/

wx:嵌入式工程师成长日记

https://mp.weixin.qq.com/s/NbYVE8hsU7V_48nbNsUByA?token=382885458&lang=zh_CNicon-default.png?t=O83Ahttps://mp.weixin.qq.com/s/NbYVE8hsU7V_48nbNsUByA?token=382885458&lang=zh_CN

ddd39e6b19e14e33897aa6213919c759.png

操作文件有专属的系统函数,系统函数并不是内核函数,因为内核函数是不允许用户使用的,系统函数就充当了二者之间的桥梁,这样用户就可以间接的完成某些内核操作了。

    在Linux系统中必须要使用系统提供的IO函数才能基于这些文件描述符完成对相关文件的读写操作。

1.open()

    open()是一个系统函数,只能在linux系统中使用, windows不支持。

    fopen() 是标准c库函数, 一般可以跨平台使用。

// 打开一个已经存在的磁盘文件int open(const char *pathname, int flags);// 打开磁盘文件, 如果文件不存在, 就会自动创建int open(const char *pathname, int flags, mode_t mode);

参数:

1.pathname: 被打开的文件的路径文件名

2.flags:使用什么方式打开指定的文件,必须要指定的属性, 以下三个属性不能同时使用, 只能任选其一:

    O_RDONLY: 以只读方式打开文件

    O_WRONLY: 以只写方式打开文件

    O_RDWR: 以读写方式打开文件

(可选属性):

    O_APPEND: 新数据追加到文件尾部, 不会覆盖文件的原来内容

    O_CREAT: 如果文件不存在, 创建该文件, 如果文件存在什么也不做

    O_EXCL: 检测文件是否存在, 必须要和O_CREAT 一起使用, 不能单独使用: O_CREAT | O_EXCL;(若检测到文件不存在, 则创建新文件,检测到文件已经存在,创建失败,函数直接返回-1)

3.mode: 在创建新文件的时候才需要指定这个参数的值,用于指定新文件的权限,这是一个八进制的整数。(该最大值为:0777)

创建的新文件对应的最终实际权限, 计算公式: (mode & ~umask)

umask 掩码可以通过 umask 命令查看

4.返回值:

    成功: 返回内核分配的文件描述符,是一个大于0的整数

    失败: -1

#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <string.h>#include <fcntl.h>
int main(){    // 打开文件    int fd = open("abc.txt", O_RDWR);    if(fd == -1)    {        printf("打开文件失败\n");    }    else    {        printf("fd: %d\n", fd);    }
    close(fd);    return 0;}

2.close()

功能:关闭文件并释放文件描述符

#include <unistd.h>int close(int fd);

3.read()

功能:用于读取文件内部数据

#include <unistd.h>ssize_t read(int fd, void *buf, size_t count);

参数:

fd: 文件描述符, open() 函数的返回值,通过这个参数定位打开的磁盘文件

buf: 传出参数, 指向一块有效的内存, 用于存储从文件中读出的数据

count: buf指针指向的内存的大小, 指定可以存储的最大字节数

返回值:

    大于0: 从文件中读出的字节数,读文件成功

    等于0: 代表文件读完了,读文件成功

    -1: 读文件失败

4.write()

功能:用于将数据写入到文件内部

#include <unistd.h>ssize_t write(int fd, const void *buf, size_t count);

参数:

fd: 文件描述符, open() 函数的返回值, 通过这个参数定位打开的磁盘文件

buf: 指向一块有效的内存地址, 里边有要写入到磁盘文件中的数据

count: 要往磁盘文件中写入的字节数, 一般情况下就是buf字符串的长度, 调用strlen(buf)即可求得

返回值:

    大于0: 成功写入到磁盘文件中的字节数

    -1: 写文件失败了

#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <string.h>#include <fcntl.h>
int main(){    // 打开存在的文件a.txt, 读这个文件    int fd1 = open("./a.txt", O_RDONLY);    if(fd1 == -1)    {        perror("error");        return -1;    }
    // 打开不存在的文件, 将其创建出来, 将从a.txt读出的内容写入这个文件中    int fd2 = open("b.txt", O_WRONLY|O_CREAT, 0664);    if(fd2 == -1)    {        perror("error");        return -1;    }
    // 循环读文件, 循环写文件    char buf[4096];    int len = -1;    while( (len = read(fd1, buf, sizeof(buf))) > 0 )    {     // 将读到的数据写入到另一个文件中        write(fd2, buf, len);     }    // 关闭文件    close(fd1);    close(fd2);
    return 0;}

5.lseek()

功能:通过这个函数移动文件指针, 也可以通过这个函数进行文件的拓展

#include <sys/types.h>#include <unistd.h>
off_t lseek(int fd, off_t offset, int whence);

参数:

fd: 文件描述符, open() 函数的返回值, 通过这个参数定位打开的磁盘文件

offset: 偏移量,需配合第三个参数使用

whence: 以下三个参数任选其中一个

    SEEK_SET: 从文件头部开始偏移 offset 个字节

    SEEK_CUR: 从当前文件指针的位置向后偏移offset个字节

    SEEK_END: 从文件尾部向后偏移offset个字节

返回值:

    成功: 文件指针从头部开始计算总的偏移量

    失败: -1

三种用法:

//文件指针移动到文件头部lseek(fd, 0, SEEK_SET);//得到当前文件指针的位置lseek(fd, 0, SEEK_CUR); //得到文件总大小lseek(fd, 0, SEEK_END);

利用lseek()实现文件扩展

注意:

使用 lseek 函数进行文件拓展必须要满足以下条件:

1.文件指针必须要偏移到文件尾部之后,多出来的就需要被填充的部分

2.文件拓展之后,必须要使用write()函数进行一次写操作(写什么都可以)

#include <stdio.h>#include <fcntl.h>#include <unistd.h>int main(){    int fd = open("a.txt", O_RDWR);    if(fd == -1)    {        perror("error");        return -1;    }
    // 文件一共拓展了 1001 个字节    lseek(fd, 1000, SEEK_END);    write(fd, " ", 1);
    close(fd);    return 0;}

6.truncate()/ftruncate()

功能:修改指定文件大小,用来拓展文件,比lseek()使用更简单        

#include <unistd.h>#include <sys/types.h>
int truncate(const char *path, off_t length);-int ftruncate(int fd, off_t length);

参数:

path: 要拓展/截断的文件的文件名

fd: 文件描述符

length: 有以下两种情况:

    ①文件原来size > length,文件被截断, 尾部多余的部分被删除, 文件最终长度为length

    ②文件原来size < length,文件被拓展, 文件最终长度为length

返回值:  

    成功则返回0

    失败则返回-1


http://www.ppmy.cn/ops/150873.html

相关文章

电商项目-基于ElasticSearch实现商品搜索功能(四)

一、 高亮显示 1.1 高亮分析 高亮显示是指根据商品关键字搜索商品的时候&#xff0c;显示的页面对关键字给定了特殊样式&#xff0c;让它显示更加突出&#xff0c;如商品搜索中&#xff0c;关键字变成了红色&#xff0c;其实就是给定了红色样式。 1.2 高亮搜索实现步骤解析 …

用python进行大恒相机的调试

一、背景 工作中需要用到大恒相机 跟大恒工程师沟通&#xff0c;拿到API接口库 二、直接上代码&#xff1a; import gxipy as gx import cv2 import numpy as np import sys #初始化设备管理器 device_manager gx.DeviceManager() #枚举设备&#xff0c;返回设备数量和设备信…

阻塞赋值和非阻塞赋值

理论学习 阻塞赋值 用 表示 &#xff0c;这种对应的电路结构常常与触发器没有关系&#xff0c;只与输入电平的变化有关系。可以将阻塞赋值的操作看作只有一个步骤的操作&#xff0c;即将计算赋值符号的右边赋值给左边&#xff0c;在未执行完之前&#…

在 .NET 9 中使用 Scalar 替代 Swagger

前言 在.NET 9发布以后ASP.NET Core官方团队发布公告已经将Swashbuckle.AspNetCore&#xff08;一个为ASP.NET Core API提供Swagger工具的项目&#xff09;从ASP.NET Core Web API模板中移除&#xff0c;这意味着以后我们创建Web API项目的时候不会再自动生成Swagger API文档了…

如何设计一个 RPC 框架?需要考虑哪些点?

面试官&#xff1a;如何设计一个 RPC 框架&#xff1f;需要考虑哪些点&#xff1f; 设计一个远程过程调用&#xff08;RPC&#xff09;框架是一个复杂的系统工程&#xff0c;涉及多个方面的考虑。一个好的 RPC 框架应具备可扩展性、灵活性、易用性和高性能。下面是设计 RPC 框…

Rust 强制类型转换和动态指针类型的转换

在 Rust 中的强制类型转换&#xff08;Coercion&#xff09;语义&#xff0c;与 Java 或 C 中的子类到父类的转换有某些相似之处&#xff0c;但两者的实现机制和使用场景有很大的区别。 我们将从 Java/C 的子类到父类转换 和 Rust 的强制类型转换 的角度进行比较&#xff0c;帮…

LeetCode 热题 100_课程表(53_207_中等_C++)(图,拓扑排序)

LeetCode 热题 100_课程表&#xff08;53_207&#xff09; 题目描述&#xff1a;输入输出样例&#xff1a;题解&#xff1a;解题思路&#xff1a;思路一&#xff08;广度优先搜索拓扑排序&#xff09;&#xff1a; 代码实现代码实现&#xff08;思路一&#xff08;拓扑排序&…

【Redis】Redis事务和Lua脚本的区别

Redis事务 概念 事务&#xff1a;Redis事务是一组命令的集合&#xff0c;这些命令会被序列化地执行&#xff0c;中间不会被其他命令插入。 MULTI/EXEC&#xff1a;Redis事务通过MULTI命令开始&#xff0c;通过EXEC命令执行所有已入队的命令。 特点 原子性&#xff1a; 事务…