文章目录
- 一、实现细节
- 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.测试
我们现在可以简单的去跑一下代码
如果我们将代码改为追加模式
那么运行结果为
所以这样我们就不用再去使用系统调用了,而是直接使用库函数即可。
这就是一层封装,在windows等其他操作系统也是一样,我们只需要根据其对应的系统调用写上对应的代码即可。这样就可以实现跨平台性了
像java中的jvm也是一样的,它本身就是用C/C++写的,然后java的代码就是跑在它的上面的
6.缓冲区的实现
在这里我们不去考虑一些异常处理,也不考虑一些局部的问题
在C语言中,即存在输入缓冲区,也有输出缓冲区。
对于输入缓冲区,就是我们在输入的时候,我们的设备只认识字符,即便是123也会将其认为字符1字符2字符3。
即对于上面的这些接口,我们也可以看到,这些都是void类型,也就是说并不关心具体是什么类型。
具体这些数据是什么类型全部交由上层来解释。所以我们的键盘显示器都叫做字符输入输出设备。所以才可以格式化输入输出
总之全部当作字符来出来
我们现在只实现一个输出缓冲区
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;
}
运行结果为
以上是行刷新的策略
我们可以试一下全刷新
其实我们发现结果不对,因为即便进程退出了也没有刷新出来
这是因为我们缺少了在进程退出时强制刷新的部分,如下所示
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 4typedef 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;
}