【Linux】第二十四站:模拟实现C语言文件标准库

文章目录

  • 一、实现细节
    • 1.需要实现的函数
    • 2.fopen的实现
    • 3.fclose
    • 4.fwrite
    • 5.测试
    • 6.缓冲区的实现
    • 7.FILE中缓冲区的意义
  • 二、完整代码

一、实现细节

1.需要实现的函数

#include "mystdio.h"

int main()
{
    _FILE* fp = _fopen("test.txt","w");
    if(fp == NULL) return 1;
    const char* msg = "hell world";

    _fwrite(fp,msg,strlen(msg));

    _fclose(fp);
    return 0;
}

如上代码所示,就是我们需要实现的功能

也就是说,我们在头文件中需要写出以下的代码

#ifndef __MYSTDIO_H__
#define __MYSTDIO_H__

#include <string.h>
typedef struct IO_FILE
{
    int fileno; 
}_FILE;
_FILE* _fopen(const char* filename, const char* flag);
int _fwrite(_FILE* fp, const char* s, int len);
void _fclose(_FILE* fp);

#endif

2.fopen的实现

它的实现逻辑是这样的,首先我们要根据对应的flag去选择以什么样的方式去打开某个文件。

当文件成功打开以后,我们需要创建这个文件的结构体对象,然后将文件描述符写入到文件结构体中。最后返回这个文件的结构体

_FILE* _fopen(const char* filename, const char* flag)
{
    assert(filename);
    assert(flag);
    int f = 0;
    int fd = -1;
    if(strcmp(flag, "w") == 0)
    {
        f = (O_WRONLY|O_CREAT|O_TRUNC);
        fd = open(filename, f, FILE_MODE);
    }
    else if(strcmp(flag, "a") == 0) 
    {
        f = (O_WRONLY|O_CREAT|O_APPEND);
        fd = open(filename, f, FILE_MODE);
    }
    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;
    return fp;
}

3.fclose

对于文件的关闭,我们只需要利用close这个系统调用接口,关闭对应的文件描述符即可,然后释放掉这个文件结构体

void _fclose(_FILE* fp)
{
    if(fp == NULL)
    {
        return;
    }
    close(fp->fileno);
    free(fp);
}

4.fwrite

int _fwrite(_FILE* fp, const char* s, int len)
{
    return write(fp->fileno,s,len);    
}

对于这个fwrite而言,它只需要利用write这个接口直接去调用这个写入的接口即可

5.测试

我们现在可以简单的去跑一下代码

image-20231130163432240

image-20231130163342862

如果我们将代码改为追加模式

image-20231130163456193

那么运行结果为

image-20231130163550599

所以这样我们就不用再去使用系统调用了,而是直接使用库函数即可。

这就是一层封装,在windows等其他操作系统也是一样,我们只需要根据其对应的系统调用写上对应的代码即可。这样就可以实现跨平台性了

像java中的jvm也是一样的,它本身就是用C/C++写的,然后java的代码就是跑在它的上面的

6.缓冲区的实现

在这里我们不去考虑一些异常处理,也不考虑一些局部的问题

image-20231130165707739

在C语言中,即存在输入缓冲区,也有输出缓冲区。

对于输入缓冲区,就是我们在输入的时候,我们的设备只认识字符,即便是123也会将其认为字符1字符2字符3。

image-20231130165832594

image-20231130165853368

即对于上面的这些接口,我们也可以看到,这些都是void类型,也就是说并不关心具体是什么类型。

具体这些数据是什么类型全部交由上层来解释。所以我们的键盘显示器都叫做字符输入输出设备。所以才可以格式化输入输出

总之全部当作字符来出来

我们现在只实现一个输出缓冲区

image-20231130175546868

flag代表着的是刷新的模式,有无刷新,行刷新,全刷新三种策略。我们分别用宏来表示

最终我们的代码改为如下

#include "mystdio.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <assert.h>

#define FILE_MODE 0666

//"w","a",r
_FILE* _fopen(const char* filename, const char* flag)
{
    assert(filename);
    assert(flag);
    int f = 0;
    int fd = -1;
    if(strcmp(flag, "w") == 0)
    {
        f = (O_WRONLY|O_CREAT|O_TRUNC);
        fd = open(filename, f, FILE_MODE);
    }
    else if(strcmp(flag, "a") == 0) 
   {
        f = (O_WRONLY|O_CREAT|O_APPEND);
        fd = open(filename, f, FILE_MODE);
    }
    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;
}
int _fwrite(_FILE* fp, const char* s, int len)
{
    memcpy(&(fp->outbuffer[fp->out_pos]), s, len);
    fp->out_pos += len;
    if(fp->flag&FLUSH_NOW)
    {
        write(fp->fileno, fp->outbuffer, fp->out_pos);
        fp->out_pos = 0;
    }
    else if(fp->flag&FLUSH_LINE)
    {
        if(fp->outbuffer[fp->out_pos-1]=='\n')
        {
            write(fp->fileno, fp->outbuffer, fp->out_pos);
            fp->out_pos = 0;
        }
    }
    else if(fp->flag&FLUSH_ALL)
    {
        if(fp->out_pos == SIZE)
        {
            write(fp->fileno, fp->outbuffer, fp->out_pos);
            fp->out_pos = 0;
        }
    }
    return len;
}
void _fclose(_FILE* fp)
{
    if(fp == NULL)
    {
        return;
    }
    close(fp->fileno);
    free(fp);
}

对于main.c中,代码改为如下

#include "mystdio.h"
#include <unistd.h>
#define Filename "test.txt"

int main()
{
    _FILE* fp = _fopen(Filename,"w");
    if(fp == NULL) return 1;
    const char* msg = "hell world\n";
    int cnt = 10;
    while(cnt)
    {
        _fwrite(fp,msg,strlen(msg));
        sleep(1);
        cnt--;
    }
    _fclose(fp);
    return 0;
}

运行结果为

image-20231130175724942

以上是行刷新的策略

我们可以试一下全刷新

image-20231130175852601

其实我们发现结果不对,因为即便进程退出了也没有刷新出来

image-20231130180102790

这是因为我们缺少了在进程退出时强制刷新的部分,如下所示

image-20231130180752407

image-20231130180738897

7.FILE中缓冲区的意义

因为,如果我们写一次就往操作系统写一次,这样效率太低,不如暂一大批,然后再跟操作系统去交互。

即让C语言的接口调用起来变得更快。

二、完整代码

mystdio.h

#ifndef __MYSTDIO_H__
#define __MYSTDIO_H__

#include <string.h>
#define SIZE 1024

#define FLUSH_NOW 1
#define FLUSH_LINE 2
#define FLUSH_ALL 4

typedef struct IO_FILE
{
    int fileno; 
    //char inbuffer[SIZE];
    //int in_pos;
    char outbuffer[SIZE];
    int out_pos;
    int flag;
}_FILE;
_FILE* _fopen(const char* filename, const char* flag);
int _fwrite(_FILE* fp, const char* s, int len);
void _fclose(_FILE* fp);

#endif

mystdio.c

#include "mystdio.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <assert.h>

#define FILE_MODE 0666

//"w","a",r
_FILE* _fopen(const char* filename, const char* flag)
{
    assert(filename);
    assert(flag);
    int f = 0;
    int fd = -1;
    if(strcmp(flag, "w") == 0)
    {
        f = (O_WRONLY|O_CREAT|O_TRUNC);
        fd = open(filename, f, FILE_MODE);
    }
    else if(strcmp(flag, "a") == 0) 
   {
        f = (O_WRONLY|O_CREAT|O_APPEND);
        fd = open(filename, f, FILE_MODE);
    }
    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->flag = FLUSH_ALL;
    fp->out_pos = 0;
    return fp;
}
int _fwrite(_FILE* fp, const char* s, int len)
{
    memcpy(&(fp->outbuffer[fp->out_pos]), s, len);
    fp->out_pos += len;
    if(fp->flag&FLUSH_NOW)
    {
        write(fp->fileno, fp->outbuffer, fp->out_pos);
        fp->out_pos = 0;
    }
    else if(fp->flag&FLUSH_LINE)
    {
        if(fp->outbuffer[fp->out_pos-1]=='\n')
        {
            write(fp->fileno, fp->outbuffer, fp->out_pos);
            fp->out_pos = 0;
        }
    }
    else if(fp->flag&FLUSH_ALL)
    {
        if(fp->out_pos == SIZE)
        {
            write(fp->fileno, fp->outbuffer, fp->out_pos);
            fp->out_pos = 0;
        }
    }
    return len;
}
void _fflush(_FILE* fp)
{
    if(fp->out_pos > 0)
    {
        write(fp->fileno, fp->outbuffer, fp->out_pos);
        fp->out_pos = 0;
    }
} 
void _fclose(_FILE* fp)
{
    if(fp == NULL)
    {
        return;
    }
    _fflush(fp);
    close(fp->fileno);
    free(fp);
}

main.c

#include "mystdio.h"
#include <unistd.h>
#define Filename "test.txt"

int main()
{
    _FILE* fp = _fopen(Filename,"w");
    if(fp == NULL) return 1;
    const char* msg = "hell world\n";
    int cnt = 10;
    while(cnt)
    {
        _fwrite(fp,msg,strlen(msg));
        sleep(1);
        cnt--;
    }
    _fclose(fp);
    return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/212839.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

2023年5月电子学会青少年软件编程 Python编程等级考试一级真题解析(判断题)

2023年5月Python编程等级考试一级真题解析 判断题(共10题,每题2分,共20分) 26、在编写较长的Python程序时,所有代码都不需要缩进,Python会自动识别代码之间的关系 答案:错 考点分析:考查python代码书写格式规范,python编写较长的程序时,需要明确严格的缩进,不然有…

医美店会员管理系统预约小程序作用是什么

医美在美业中占据着一定地位&#xff0c;爱美使然和经济独立、悦己消费下&#xff0c;不少女性会前往医美机构做脸部整容、嫩肤补水等服务&#xff0c;如美容院一样都是具备本地外地属性的&#xff0c;因此在如今互联网盛行下&#xff0c;商家需要借势线上破解难题及增强生意效…

时序预测 | Python实现LSTM长短期记忆神经网络时间序列预测(多图,多指标)

时序预测 | Python实现LSTM长短期记忆神经网络时间序列预测(多图,多指标) 目录 时序预测 | Python实现LSTM长短期记忆神经网络时间序列预测(多图,多指标)预测效果基本介绍环境准备程序设计参考资料预测效果 基本介绍 LSTM是一种递归神经网络(RNN)的变体

FL Studio2024中文语言版水果编曲软件

FL Studio21.2这款软件在国内被广泛使用&#xff0c;因此又被称为"水果"。它提供音符编辑器&#xff0c;可以针对作曲者的要求编辑出不同音律的节奏&#xff0c;例如鼓、镲、锣、钢琴、笛、大提琴、筝、扬琴等等任何乐器的节奏律动。此外&#xff0c;它还提供了方便快…

linux 安装go环境

下载go SDK All releases - The Go Programming Language 此处建议选择与本机windows一样的版本&#xff0c;便于调试&#xff0c;若不涉及本地windows&#xff0c;则忽略此提示 上传到linux 解压go SDK 执行下述命令进行解压 tar -xvf go1.19.linux-amd64.tar.gz 此处选择…

Vue Proxy配置代理服务器

一.跨域问题 1.1 什么是跨域问题&#xff1f; 浏览器从一个域名的网页去请求另一个域名的资源时&#xff0c;域名、端口、协议任一不同&#xff0c;都会导致跨域问题。即前端接口去调用不在同一个域内的后端服务器而产生的问题。 1.2 如何解决跨域问题&#xff1f; — 代理服务…

C++函数模板,类模板

C函数模板&#xff0c;类模板 1.函数模板1.1函数模板的概念1.2函数模板的格式1.3函数模板的原理1.4函数模板的实例化1.5模板参数的匹配原则 2.类模板2.1类模板的定义格式2.2类模板的实例化 1.函数模板 1.1函数模板的概念 在C中&#xff0c;函数模板是一种通用的函数定义&…

Micropython for QNX编译过程

Micropython for QNX编译过程 执行步骤 1. https://github.com/micropython/micropython select tag 1.20.0 git clone micropython 2. make -C mpy-cross 3. 修改py/mkenv.mk CROSS_COMPILE ntoaarch64- 注意如果这步必须在make -C mpy-cross 之后执行&#xff0c;如果需要重…

Hdoop学习笔记(HDP)-Part.11 安装Kerberos

目录 Part.01 关于HDP Part.02 核心组件原理 Part.03 资源规划 Part.04 基础环境配置 Part.05 Yum源配置 Part.06 安装OracleJDK Part.07 安装MySQL Part.08 部署Ambari集群 Part.09 安装OpenLDAP Part.10 创建集群 Part.11 安装Kerberos Part.12 安装HDFS Part.13 安装Ranger …

SmartSoftHelp8,代码版权保护

1.Html网页前端添加作者开发信息 2. Html网页添加版权信息 3. Css添加作者开发信息 4. JavaScript添加作者开发信息 5. C井后端代码添加作者开发信息 6. Dll内裤添加作者开发信息 7.应用程序添加开发作者信息&#xff0c;著作权&#xff0c;应用版权信息 下载地址&#…

softmax实现

import matplotlib.pyplot as plt import torch from IPython import display from d2l import torch as d2lbatch_size 256 train_iter,test_iter d2l.load_data_fashion_mnist(batch_size) test_iter.num_workers 0 train_iter.num_workers 0 num_inputs 784 # 将图片…

详细了解 MOSFET 晶体管

MOSFET 开关晶体管 MOS 管是 “金属&#xff08;Metal&#xff09;氧化物&#xff08;Oxide&#xff09;半导体&#xff08;Semi&#xff09;” 场效应晶体管&#xff0c;或者称是 “金属&#xff08;Metal&#xff09;绝缘体&#xff08;Insulator&#xff09;半导体&#xf…

JVM——垃圾回收器(G1,JDK9默认为G1垃圾回收器)

1.G1垃圾回收器 JDK9之后默认的垃圾回收器是G1&#xff08;Garbage First&#xff09;垃圾回收器。 Parallel Scavenge关注吞吐量&#xff0c;允许用户设置最大暂停时间 &#xff0c;但是会减少年轻代可用空间的大小。 CMS关注暂停时间&#xff0c;但是吞吐量方面会下降。 而G1…

C++ Easyx 三子棋

目录 思路 框架​编辑 读取操作 数据操作 绘制画面 游戏的数据结构 用二维数组来模拟棋盘格 赢的情况 平局情况 Code 代码细节部分 &#xff08;1&#xff09;初始化棋盘格 &#xff08;2&#xff09; 初始化棋子类型​编辑 事件处理部分 落子 框架内代码的完善 数据处…

【bat】批处理脚本大全

目录 1.概述 2.变量 3.运算符 3.2.重定向运算符 3.3.多命名运算符 3.4.管道运算符 4.命令 4.1.基本命令 4.2.参数传递 4.3.查看脚本内容 4.4.注释 4.5.日期和时间 4.6.启动脚本 4.7.调用其他bat 4.8.任务管理 4.8.1.任务列表查看 4.8.2.任务终止 4.9.文件夹 …

免费最新6款热门SEO优化排名工具

网站的存在感对于业务和品牌的成功至关重要。在众多网站推广方法中&#xff0c;搜索引擎优化&#xff08;SEO&#xff09;是提高网站可见性的关键。而SEO的核心之一就是关键词排名。为了更好地帮助您优化网站。 SEO关键词排名工具 在如今信息过载的互联网时代&#xff0c;用户…

CRM在设备制造行业的应用,优化资源配置

设备制造业竞争激烈&#xff0c;公司要以客户为中心&#xff0c;搞好售后服务。CRM管理软件是设备制造业客户关系管理的重要工具。以下是CRM在设备制造业里的典型应用。 1.营销管理 制订市场策略&#xff1a;设备制造通常涉及较长的决策周期和销售周期。客户可能会在多家供货商…

创意项目开源,文生图、排序/优选等...,持续开源更新!!

热烈欢迎大家在git上star&#xff01;&#xff01;&#xff01;冲鸭&#xff01;&#xff01;&#xff01; 1.dalle1在厨房家具中文场景上训练。 GitHub - leeguandong/DALLE1: dalle1在中文家具场景的微调&#xff0c;效果并不好dalle1在中文家具场景的微调&#xff0c;效果…

vite脚手架,手写实现配置动态生成路由

参考文档 vite的glob-import vue路由配置基本都是重复的代码&#xff0c;每次都写一遍挺难受&#xff0c;加个页面就带配置下路由 那就利用 vite 的 文件系统处理啊 先看实现效果 1. 考虑怎么约定路由&#xff0c;即一个文件夹下&#xff0c;又有组件&#xff0c;又有页面&am…

2 文本分类入门:TextCNN

论文链接&#xff1a;https://arxiv.org/pdf/1408.5882.pdf TextCNN 是一种用于文本分类的卷积神经网络模型。它在卷积神经网络的基础上进行了一些修改&#xff0c;以适应文本数据的特点。 TextCNN 的主要思想是使用一维卷积层来提取文本中的局部特征&#xff0c;并通过池化操…