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

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

相关推荐
啊森要自信8 小时前
CANN ops-cv:AI 硬件端视觉算法推理训练的算子性能调优与实战应用详解
人工智能·算法·cann
仟濹9 小时前
算法打卡day2 (2026-02-07 周五) | 算法: DFS | 3_卡码网99_计数孤岛_DFS
算法·深度优先
驭渊的小故事9 小时前
简单模板笔记
数据结构·笔记·算法
开开心心就好9 小时前
发票合并打印工具,多页布局设置实时预览
linux·运维·服务器·windows·pdf·harmonyos·1024程序员节
YuTaoShao9 小时前
【LeetCode 每日一题】1653. 使字符串平衡的最少删除次数——(解法一)前后缀分解
算法·leetcode·职场和发展
VT.馒头9 小时前
【力扣】2727. 判断对象是否为空
javascript·数据结构·算法·leetcode·职场和发展
goodluckyaa9 小时前
LCR 006. 两数之和 II - 输入有序数组
算法
孤狼warrior9 小时前
YOLO目标检测 一千字解析yolo最初的摸样 模型下载,数据集构建及模型训练代码
人工智能·python·深度学习·算法·yolo·目标检测·目标跟踪
予枫的编程笔记9 小时前
【Linux进阶篇】从基础到实战:grep高亮、sed流编辑、awk分析,全场景覆盖
linux·sed·grep·awk·shell编程·文本处理三剑客·管道命令
Sheep Shaun9 小时前
揭开Linux的隐藏约定:你的第一个文件描述符为什么是3?
linux·服务器·ubuntu·文件系统·缓冲区