Linux系统编程系列之模拟文件操作


前言

我们知道fopen、fwrite这些函数底层封装了open、write,那我们可以自己用操作系统提供的接口来实现fopen这些函数,简单实现一个Mystdio.h的库。本次内容会封装fopen,fwrite,fflush,fclose,当然FILE类型也需要自己封装,是对前几次知识的综合。 另外还会有个popen,是啥不重要,进程间通信再说。


一、_FILE类型

我们知道FILE类型一定有fd,缓冲区,为了方便这里只搞输出缓冲区,也需要相应的pos记录缓冲区中字符串写到了哪,当然也还需要缓冲类型,全缓冲、行缓冲还是无缓冲。

定义三个宏表示缓冲方式。其余接口均按照库函数标准来的。

cpp 复制代码
#ifndef __MYSTDIO_H__
#define __MYSTDIO_H__

#include<string.h>
#define FLUSH_NOW 1
#define FLUSH_LINE 2
#define FLUSH_ALL 4
#define SIZE 1024
typedef struct IO_FILE{
  int _fileno; 
  int flag;
  //char inbuff[SIZE];
  //int in_pos
  char outbuff[SIZE]; //输出缓冲区

  int out_pos;

}_FILE;

_FILE *_fopen(const char* filename,const char* flag);

int _fwrite(const void *ptr,size_t size,int n,_FILE* fp);

void _fclose(_FILE* fp);
#endif
cpp 复制代码
#ifndef __MYSTDIO_H__
#define __MYSTDIO_H__
#endif

这个是条件编译,防止头文件被重复包含,一定要加endif.

二、函数实现

_fopen

FILE*是底层给我们malloc出来的,我们也打开文件之后,malloc一个就可以。

至于flag, w就是O_CREAT|O_WRONLY|O_TRUNC ,其他都差不多。

默认给行缓冲了,这里不额外判断。

cpp 复制代码
_FILE*_fopen(const char* filename,const char* flag)
{
   int f = 0;   
   int fd = -1;
   if(strcmp(flag,"w") == 0)
   {
     f = (O_CREAT|O_WRONLY|O_TRUNC);
     fd = open(filename,f,0666);
   }
   else if(strcmp(flag,"a") == 0)
   {
     f = (O_CREAT|O_WRONLY|O_APPEND);
     fd = open(filename,f,0666); 
   } 
   else if(strcmp(flag,"r") == 0)
   {
     f = (O_RDONLY);
     fd = open(filename,f);
   }
   else return NULL;  
   if(fd == -1) {
     return NULL;
   } 

   _FILE* fp = (_FILE*)malloc(sizeof(_FILE));
   if(fp == NULL) return NULL;
   fp->_fileno = fd;
   fp->flag = FLUSH_LINE;
   fp->out_pos = 0;
     
   return fp;
}

_fwrite

fwrite要考虑缓冲方式,进行判断 if(flag & ...) ()

这里没有考虑n,一般传过来的都是1,先拷贝到缓冲区,给out_pos相应的值。

这里行缓冲的方式稍微有点问题,毕竟有可能是一行字符串中间带换行,自行修改即可。

cpp 复制代码
int _fwrite(const void *ptr,size_t size,int n,_FILE* fp)
{
  //先不考虑n  
    memcpy(&fp->outbuff[fp->out_pos],ptr,size);
    fp->out_pos += size;
    if(fp->flag & FLUSH_NOW)
    {
      write(fp->_fileno,fp->outbuff,fp->out_pos);
      fp->out_pos = 0;
    }
    else if((fp->flag & FLUSH_LINE ))
    {
      if(fp->outbuff[fp->out_pos - 1] == '\n')
      {
       write(fp->_fileno,fp->outbuff,fp->out_pos);
       fp->out_pos = 0;
      }
      else{
        return size;
      }
    }
    else if(fp->flag & FLUSH_ALL)
    {
      if(fp->out_pos == SIZE)
      {
      write(fp->_fileno,fp->outbuff,fp->out_pos);
      fp->out_pos = 0;
      }
      else{
        return size;
      }
    }
    
    return size;
}

fflush 和 fclose

fflush相当于进行一次无缓冲写,比较好写。

注意_fclose的时候要进行一次刷新缓冲区,关闭文件、释放自己malloc出来的FILE*

cpp 复制代码
void _fflush(_FILE* fp)
{
 if(fp->out_pos > 0)
 {
    write(fp->_fileno,fp->outbuff,fp->out_pos);
  fp->out_pos = 0;
 }
}
void _fclose(_FILE* fp)
{
  if(fp == NULL) return;
  _fflush(fp);
  close(fp->_fileno);
  free(fp);
}

测试

cpp 复制代码
#include<bits/stdc++.h>
#include<unistd.h>
#include<sys/types.h>
#include "Mystdio.h"
using namespace std;
int main()
{
 _FILE *fp = _fopen("test.txt","w");
 if(fp == NULL) return 1;

 const char* msg = "hello world\n";
// _fwrite(fp,msg,strlen(msg));
 int cnt = 10;
while(cnt--)
{
_fwrite(msg,strlen(msg),1,fp);
 //fflush(fp);
 sleep(1);
}
 _fclose(fp);

  return 0;
}

注意:如果使用makefile生成可执行需要两个文件,一个是测试文件,一个是实现文件。

三、popen

cpp 复制代码
#pragma once
#include<unistd.h>
#include<cstring>
#include<stdlib.h>
#include<bits/stdc++.h>
#include<cstdio>

FILE* Mypopen(const char* cmd,const char* mod)
{
    if(strcmp(mod,"r") != 0) {

        return nullptr;
    }
   int pipe_fd[2];
   pid_t pid;
   FILE* fp = NULL;

   if(pipe(pipe_fd) == -1)
   {
     perror("create pipe fail") ;
     return nullptr;
   }

   pid = fork();
   if(pid < 0)
   {
    perror("fork fail");
    close(pipe_fd[0]);
    close(pipe_fd[1]);
    return nullptr;
   }

   if(pid == 0)
   {
     dup2(pipe_fd[1],STDOUT_FILENO);
     close(pipe_fd[0]);
    close(pipe_fd[1]);

    char* tmp[] = {const_cast<char*>(cmd),nullptr};
    execvp(cmd,tmp);

    perror("execvp fail");
   }
   else
   {
     close(pipe_fd[1]);
     fp = fdopen(pipe_fd[0],mod);
     if(fp == nullptr)
     {
        //
        return nullptr;
     }
   }
   return fp;
}

具体是干什么的后面再说,进程间通信中会提到。

相关推荐
chenyuhao202420 小时前
Linux网络编程:数据链路层
linux·运维·网络
萤虫之光20 小时前
有序数组中的单一元素(一)
数据结构·算法
QT 小鲜肉20 小时前
【Linux命令大全】002.文件传输之uupick命令(实操篇)
linux·运维·服务器·chrome·笔记
QT 小鲜肉20 小时前
【Linux命令大全】003.文档编辑之colrm命令(实操篇)
linux·运维·服务器·chrome·笔记
颜酱20 小时前
从经典问题入手,吃透动态规划核心(DP五部曲实战)
前端·javascript·算法
QT 小鲜肉20 小时前
【Linux命令大全】002.文件传输之uucico命令(实操篇)
linux·运维·服务器·chrome·笔记·github
WBluuue20 小时前
AtCoder Beginner Contest 438(ABCDEF)
c++·算法
Murphy_3121 小时前
从根上了解一下复指数
算法
Run_Teenage21 小时前
Linux:理解IO,重定向
linux·运维·算法