文件操作详解

C语言(标准库IO接口)

打开、关闭文件

在这里插入图片描述

  • path:文件路径名
  • mode
    r:只读
    r+:可读可写
    w:只写,文件不存在则创建,若文件存在则清空原有内容
    w+:相较于w,多出了可读操作
    a:追加写打开,若文件不存在则创建,每次总是写到文件末尾
    a+:相较于a,多出了可读操作
  • 返回值:文件的操作句柄(文件流指针)

在这里插入图片描述

  • fp:文件的操作句柄
读写文件

在这里插入图片描述

  • ptr:用于接收数据的缓冲区
  • size:一次要读取的字节大小
  • nmemb:一次要读取多少个 size
  • stream:fopen返回的文件流指针
  • 返回值:成功读取到的字节大小    若读取到文件末尾:返回0

在这里插入图片描述

  • ptr:要写入的数据
  • size:一次要写入的字节大小
  • nmemb:一次要写入多少个 size
  • stream:fopen返回的文件流指针
  • 返回值:实际写入的块个数
文件指针位置重定位

在这里插入图片描述

  • 对文件的读写位置跳转到whence位置偏移offset个字节处
  • stream:文件的操作句柄
  • whence:跳转的起始位置
    SEEK_SET:文件起始位置
    SEEK_CUR:当前读写位置
    SEEK_END:文件末尾位置
  • offset:偏移量
测试用例
#include <stdio.h>
#include <string.h>

int main()
{
    FILE* fp = fopen("./myfile.txt", "r+");
    if(!fp)
    {
        printf("fopen error\n");
        return -1;
    }

    const char* str = "hello world\n";
    size_t wnum = fwrite(str, 1, strlen(str), fp);
    if(wnum == 0)
    {
        printf("fwrite error\n");
        return -1;
    }
    printf("wnum:%d\n", (int)wnum);

    //跳转到文件的起始位置,否则会读取不到数据,因为此时的文件指针在末尾
    fseek(fp, 0, SEEK_SET);

    char buf[1024] = {0};
    size_t rnum = fread(buf, 1, sizeof(buf) - 1, fp);
    printf("rnum:%d --- buf:%s\n", (int)rnum, buf);

    //关闭文件
    fclose(fp);
    return 0;
}

Linux(系统调用IO接口)

打开、关闭文件

在这里插入图片描述

  • pathname:文件路径名
  • flags:选项参数,文件的打开方式,必选项、可选项
    必选项(只能选择其一):O_RDONLY—只读    O_WRONLY—只写    O_RDWR—读写
    可选项:
    O_CREAT:若文件存在则打开,否则创建新文件
    O_EXCL:与O_CREAT同时使用,若文件存在则报错,不存在则创建新文件
    O_TRUNC:打开文件的同时清空原有内容
    O_APPEND:追加写,总是将数据写入到文件末尾
  • mode:文件的权限,如果使用了O_CREAT有可能创建新文件,就一定要指定文件权限,八进制数字形式
  • 返回值:一个非负整数,文件描述符,文件的操作句柄    错误:-1

在这里插入图片描述
fd:文件的操作句柄

读写文件

在这里插入图片描述

  • fd:文件的操作句柄
  • buf:要写入文件的数据的空间首地址
  • count:要写入的数据大小
  • 返回值:返回实际写入文件的数据字节长度    错误:-1

在这里插入图片描述

  • fd:文件的操作句柄
  • buf:从文件中读取数据放到哪块缓冲区中的首地址
  • len:想要读取的数据长度,注意这个len不能大于缓冲区的大小
  • 返回值:返回的是实际读取到的数据字节长度    错误:-1
文件指针位置重定位

在这里插入图片描述

  • fd:文件的操作句柄
  • offset:偏移量
  • whence:从哪里开始偏移
    SEEK_SET:文件的起始位置
    SEEK_CUR:文件当前读写位置
    SEEK_END:文件末尾
  • 返回值:成功返回当前位置相对于起始位置的偏移量    失败:-1
测试用例
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <sys/stat.h>

int main()
{   
    umask(0);
    int fd = open("./myfile.txt", O_RDWR | O_CREAT, 0777);
    if(fd < 0)
    {
        perror("open error");
        return -1;
    }

    //每次将写位置跳转到文件末尾,相当于追加写
    lseek(fd, 0, SEEK_END);

    const char* str = "hello world\n";
    ssize_t ret = write(fd, str, strlen(str));
    if(ret < 0)
    {
        perror("write error");
        return -1;
    }
    printf("ret:%d\n", (int)ret);

    //跳转到文件的起始位置,否则会读取不到数据,因为此时文件指针在末尾
    lseek(fd, 0, SEEK_SET);

    char buf[1024] = {0};
    ret = read(fd, buf, sizeof(buf) - 1);
    if(ret < 0)
    {
        perror("read error");
        return -1;
    }
    printf("ret:%d---buf:%s\n", (int)ret, buf);

    //关闭文件
    close(fd);
    return 0;
}

文件描述符

文件描述符:非负整数(内核中文件描述信息结构体数组的下标)

进程通过 struct file 结构体来描述打开的文件,使用了 struct file *fd_array[]; 文件描述符就是这个数组的下标;用户打开文件,操作系统通过 struct file 结构体描述文件,并且将指针添加进 fd_array 数组中,向用户返回这个文件描述信息在数组中位置(下标),用户操作文件的时候,将这个下标传递给操作系统,操作系统通过下标找到文件描述信息进而操作文件。
在这里插入图片描述

为什么打开一个文件后,如果不操作了一定要关闭,释放资源?
文件描述符实际是有限的,若不关闭文件,文件描述符用完了后,则在进程中就打不开新文件了。

文件描述符的分配规则:最小未使用

一个程序运行起来后,进程中默认打开三个文件:标准输入stdin(0),标准输出stdout(1),标准错误stderr(2)

标准输入标准输出标准错误
012文件描述符(int)
stdinstdoutstderr文件流指针(FILE*)

测试用例:

//这个demo体会关闭1号文件描述符,即标准输出

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

int main()
{
    //将文件权限掩码设置为0
    umask(0);

    //关闭标准输出文件描述符
    close(1);

    int fd = open("./test.txt", O_RDWR | O_CREAT, 0664);
    if(fd < 0)
    {
        perror("open error");
        return -1;
    }

    printf("fd = %d\n", fd);
    fflush(stdout);

    //关闭文件,释放资源
    close(fd);
    return 0;
}

在这里插入图片描述

从上述例子我们可以发现 printf 并非真的一定要把数据写入标准输出文件,而是因为 printf 函数中操作文件的时候操作的描述符是1。原本 向1中写入数据就是向标准输出写入,然后当1指向了 新的文件后,这时候 printf 就会将数据写入到指定的新的文件中。

重定向
将数据不再写入原本的文件,而是写入新的指定的文件中,实现方式就是替换这个描述符对应的文件描述信息。实际是描述符的重定向,改变描述符所指向的文件,就改变了数据的流向。

描述符重定向函数:
在这里插入图片描述
让 newfd 这个描述符也指向 oldfd 所指向的文件,这时候 oldfd 和 newfd 都能够操作 oldfd 所指向的文件。

测试用例:

//这个demo体会关闭1号文件描述符,即标准输出

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

int main()
{
    //将文件权限掩码设置为0
    umask(0);

    int fd = open("./test_dup2.txt", O_RDWR | O_CREAT, 0664);
    if(fd < 0)
    {
        perror("open error");
        return -1;
    }

    //将1号文件描述符也指向fd所描述的文件
    dup2(fd, 1);

    printf("fd = %d\n", fd);
    fflush(stdout);

    //关闭文件,释放资源
    close(fd);
    return 0;
}

在这里插入图片描述

文件描述符与文件流指针的关系
  • 库函数的操作句柄是文件流指针
  • 系统调用接口的操作句柄是文件描述符

库函数是对系统调用接口的一层封装,通过文件流指针进行最终文件操作的时候,依然还要能够找到文件对应的文件描述符才可以 。文件流指针是一个结构体,结构体中有很多成员变量,其中就有一个叫 _fileno ,这就是文件描述符。
在这里插入图片描述
库函数接口向文件中写入数据,并不会直接写入文件,而是先写入缓冲区中,刷新缓冲区的时候才会写入文件。
系统调用接口是直接将数据写入文件的,系统调用接口没有这个缓冲区的。只有库函数才存在这个缓冲区。

测试用例:

//这个demo体会库函数接口和系统调用接口 缓冲区的有无及输出

#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>

int main()
{
    //库函数接口必须刷新缓冲区后才会将数据打印,程序退出前会刷新缓冲区,所以最后打印
    fprintf(stdout, "%s---%d", "fprintf", 1);
    fwrite("fwrite", 1, 6, stdout);

    printf("printf");

    //系统调用接口直接将数据写入到文件中,所以先打印
    write(1, "write", 5);

    sleep(3);
    return 0;
}

在这里插入图片描述

文件系统

Linux下的 ext2 文件系统为例。文件系统就是磁盘上管理文件的系统。
在这里插入图片描述
存储一个文件需要找到空闲的磁盘块存储文件数据,以及需要找到一个未被使用的 inode 节点存储自己的元信息。
针对每一个磁盘块做一个整体的位图,这样就可以快速找到空闲的磁盘块。

文件的权限就是使用位图存储,一个用户没有权限也就是 0/1 的关系,位图可以节省大量的空间以及二进制的与或非速度非常快。

文件的存储过程:通过inode_bitmap在inode区域获取空闲inode节点,通过data_bitmap获取空闲数据块,在inode结点中记录文件信息以及数据块位置,并且将文件数据写入到数据块中,将自己的目录项信息(inode节点号+文件名)添加到所在目录文件中。

目录文件:一个文件–>文件中记录的目录下的文件信息(文件名+inode节点号)–>目录项

文件的查找过程:通过目录项信息快速找到文件的inode节点号,通过inode节点号可以在inode table中快速找到文件的inode节点;通过inode节点可以找到文件数据存储位置,进而获取到数据。

软链接、硬链接文件

给一个源文件创建一个软链接文件/硬链接文件,就可以通过被创建出来的软链接文件/硬链接文件来操作源文件

为源文件创建一个硬链接文件:ln failname.txt failname.hard
为源文件创建一个软链接文件:ln -s failname.txt failname.soft

软链接文件和硬链接文件的区别 :
硬链接文件:本质上和源文件没有什么不同,都是一个文件的名称,与源文件共用同一个 inode节点,通过自己的 inode节点访问源文件数据。
软链接文件:本质上是一个独立的文件,有自己的 inode节点,文件数据中保存源文件的路径,通过这个路径访问源文件的数据。

删除上的区别:
删除源文件,软链接失效,硬链接文件只是链接数 -1,链接数(一个 inode节点对应有几个目录项)
删除一个文件,文件并不会立即被删除,而是直接删除了目录项信息,inode中的链接数 -1,此后链接数为 0 时,才会真正删除文件

软链接文件可以跨分区,硬链接文件不可以
软链接文件可以对目录创建,硬链接不可以

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值