深入理解C语言的文件【系统编程】

几个概念:

1.fopen、fclose、fread等都是库函数,而库函数需要被调用系统调用接口调用(下文会介绍的open,close,read等),所以我们可以认为f#系列的函数都是对系统调用的封装,方便二次开发
2.文件存放在磁盘里,磁盘是硬件,想访问硬件只能通过操作系统,所以所有人想访问磁盘,只能使用OS提供的接口,类似于python,c++都有不同的文件操作接口,但是其本质都是封装了调用系统调用
3.文件的操作可以看成是进程和被打开文件的关系(下面会介绍)

我们先复习一下常用的几个文件打开方式

“r”      只读打开一个文本文件,只允许读数据
“w”      只写打开或建立一个文本文件,只允许写数据
“a”      追加打开一个文本文件,并在文件末尾写数据
“rb”      只读打开一个二进制文件,只允许读数据
“wb”       只写打开或建立一个二进制文件,只允许写数据
“ab”       追加打开一个二进制文件,并在文件末尾写数据
“r+”      读写打开一个文本文件,允许读和写
“w+”      读写打开或建立一个文本文件,允许读写
“a+”      读写打开一个文本文件,允许读,或在文件末追加数据
“rb+”      读写打开一个二进制文件,允许读和写
“wb+”      读写打开或建立一个二进制文件,允许读和写
“ab+”      读写打开一个二进制文件,允许读,或在文件末追加数

接口介绍

一、open

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);
pathname: 要打开或创建的目标文件
flags: 打开文件时,可以传入多个参数选项,用下面的一个或者多个常量进行“或”运算,构成flags。
参数:
O_RDONLY: 只读打开
O_WRONLY: 只写打开
O_RDWR : 读,写打开
这三个常量,必须指定一个且只能指定一个
O_CREAT : 若文件不存在,则创建它。需要使用mode选项,来指明新文件的访问权限
O_APPEND: 追加写返回值:
成功:新打开的文件描述符
失败:-1

这个flag就是位图,所以在我们使用的时候只需要|上各种条件我们就可以实现各种效果
fclose对应close->man 3 close查看

#include<stdio.h>
#include<unistd.h>
#include<sys/wait.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#define FILE_NAME "log.txt"
int main()
{
  //这个才对应于C语言的写
  int fd=open(FILE_NAME,O_WRONLY|O_CREAT);
  //失败就返回-1
  if(fd<0)
  {
    perror("open");
    return 1;
  }
  close(fd);
  return 0;
}

我们正常创建会遇到如下情况:log.txt权限是乱码
image.png
主要原因在于我们创建文件默认权限是666目录权限是777,然后&~umask,创建文件默认权限为什么是666呢?原因就在于我们需要自己设置mode,也就是int open(const char *pathname, int flags, mode_t mode);

#include<stdio.h>
#include<unistd.h>
#include<sys/wait.h>

#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#define FILE_NAME "log.txt"
int main()
{
  //我们可以自己设置umask,这改的是子进程的umask,不影响父进程
  umask(0);
  //这个才对应于C语言的写
  int fd=open(FILE_NAME,O_WRONLY|O_CREAT,0666);
  //失败就返回-1
  if(fd<0)
  {
    perror("open");
    return 1;
  }
  close(fd);
  return 0;
}

image.png
追加需要写成
int fd=open(FILE_NAME,O_WRONLY|O_APPEND);

二、write

man 2 write查看
image.png
注意这个buf,在C语言中我们文件的写分为二进制和文本文件,但是系统调用中只有二进制,所以也证明了C语言中的写是封装了write

#include<stdio.h>
#include<unistd.h>
#include<sys/wait.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<string.h>
#define FILE_NAME "log.txt"
int main()
{
  //我们可以自己设置umask,这并不会影响shell的umask,因为这个是子进程创建的
  umask(0);
  //O_TRUNC对应于每次打开文件情况文件
  int fd=open(FILE_NAME,O_WRONLY|O_CREAT|O_TRUNC,0666);
  //失败就返回-1
  if(fd<0)
  {
    perror("open");
    return 1;
  }
  char outBuffer[64];
  int cnt=5;
  while(cnt)
  {
    sprintf(outBuffer,"%s:%d\n","hello Linux",cnt--);
    //这里不能写+1
    //虽然字符串以\0结束,但是文件不以\0结束
    write(fd,outBuffer,strlen(outBuffer));
  }
  close(fd);
  return 0;
}

image.png

三、read

man 2 read查看
image.png

#include<stdio.h>
#include<unistd.h>
#include<sys/wait.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<string.h>
#define FILE_NAME "log.txt"
int main()
{
  //我们可以自己设置umask,这并不会影响shell的umask,因为这个是子进程创建的
  umask(0);
  //O_TRUNC对应于每次打开文件情况文件
  int fd=open(FILE_NAME,O_RDONLY,0666);
  //失败就返回-1
  if(fd<0)
  {
    perror("open");
    return 1;
  }
  char buffer[1024];
  ssize_t num=read(fd,buffer,sizeof(buffer)-1);
  //buffer这里是void*类型,所以可以读到任意类型,可以是图片,这里我们认为读到的是字符串
  //read的返回值是实际读到的大小
  if(num>0)
    //在C语言中fgets是会自动加上\0的(如果文件中是字符串)
    //其实就是C语言封装的系统调用自己帮我们做了(加上\0)
    buffer[num]=0;
  printf("%s",buffer);
  close(fd);
  return 0;
}

image.png

四、原理

我们先输出一下open返回值

#include<stdio.h>
#include<unistd.h>
#include<sys/wait.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<string.h>
#define FILE_NAME(number) "log.txt" #number
int main()
{

  int fd0=open(FILE_NAME(1),O_WRONLY|O_CREAT|O_APPEND,0666);
  int fd1=open(FILE_NAME(1),O_WRONLY|O_CREAT|O_APPEND,0666);
  int fd2=open(FILE_NAME(1),O_WRONLY|O_CREAT|O_APPEND,0666);
  int fd3=open(FILE_NAME(1),O_WRONLY|O_CREAT|O_APPEND,0666);
  int fd4=open(FILE_NAME(1),O_WRONLY|O_CREAT|O_APPEND,0666);
  printf("%d\n",fd0);
  printf("%d\n",fd1);
  printf("%d\n",fd2);
  printf("%d\n",fd3);
  printf("%d\n",fd4);
  close(fd0);
  close(fd1); 
  close(fd2);
  close(fd3);
  close(fd4);
  return 0;
}

image.png
我们可以同时打开多个文件,所以被打开的文件需要被OS管理起来,OS需要先描述在组织,OS为了管理对应打开的文件,必定要为文件创建对应的内核数据结构标识文件->struct file{} 包含了文件的大部分属性
fd为什么从3开始呢?
要知道这一点我们先要知道三个标准输入输出流
stdin->键盘
stdout->显示器
stderr->显示器
image.png
所以我们可以得知C语言的FILE虽然是个结构体,但是里面必定有个字段有文件描述符

  printf("stdin->fd:%d\n",stdin->_fileno);
  printf("stdout->fd:%d\n",stdout->_fileno);
  printf("stderr->fd:%d\n",stderr->_fileno);

所以证明C语言的FILE*也封装了文件描述符
image.png
回到一开始的概念:.文件的操作可以看成是进程和被打开文件的关系,这张图清楚的展示了这样的关系
image.png

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值