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;
}

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

相关推荐
不吃土豆的马铃薯5 小时前
Spdlog 进阶:日志基本控制、日志格式控制、异步记录器
linux·服务器·开发语言·前端·c++
疯狂成瘾者5 小时前
常见的 Linux 版本
linux·运维·服务器
szxinmai主板定制专家5 小时前
基于ZYNQ MPSOC图像采集与压缩系统总体设计方案
linux·arm开发·人工智能·嵌入式硬件·fpga开发
水木流年追梦5 小时前
大模型入门-大模型的推理策略
开发语言·python·算法·正则表达式·prompt
生成论实验室5 小时前
用事件关系网络重新理解AI(三):激活函数、微调与元学习
人工智能·学习·算法·语言模型·可信计算技术
Narv工程师5 小时前
嵌入式机器人控制器算力评估:从DMIPS到WCET的完整指南
人工智能·算法·机器学习
蒟蒻的贤5 小时前
实训1227
算法
liulilittle5 小时前
TCP UCP:基于卡尔曼滤波的BBR增强型拥塞控制算法
linux·网络·c++·tcp/ip·算法·c·通讯
LCG元5 小时前
RAG - 大模型检索增强生成深度解析:本地知识库 / 企业级落地优化配置
人工智能·算法
龙佚5 小时前
RTC语音质量优化实战:搭建完整语音系统
算法·架构