【Linux系统编程】第二十八弹---构建基础文件操作库与理解标准错误流(stderr)在C与C++中的应用

小林熬夜学编程 2024-10-09 12:05:02 阅读 75

✨个人主页: 熬夜学编程的小林

💗系列专栏: 【C语言详解】 【数据结构详解】【C++详解】【Linux系统编程】

目录

1、封装简单的库

1.1、定义文件结构

1.2、打开文件

1.3、刷新缓冲区

1.4、写文件

1.5、关闭文件

1.6、各文件代码 

2、stderr

2.1、C语言代码演示

2.2、C++代码演示


1、封装简单的库

1.1、定义文件结构

<code>#define LINE_SIZE 1024

#define FLUSH_NOW 1 // 立即刷新

#define FLUSH_LINE 2 // 行刷新

#define FLUSH_FULL 4 // 全缓冲

typedef struct _myFILE

{

unsigned int flags;// 文件刷新方式

int fileno;// fd

// 缓冲区

char cache[LINE_SIZE];

int cap;// 容量

int pos;// 下次写入的位置

}myFILE;// C语言创建结构体变量需要加struct关键字,因此使用typedef重命名

1.2、打开文件

打开文件本质是开辟一块存放文件数据的空间。

myFILE* my_fopen(const char* path,const char* flag)

{

int flag1 = 0;// 系统调用的文件打开方式

int iscreate = 0;// 文件是否被创建

mode_t mode = 0666;// 默认权限设置

// 读方式打开文件,只需设置flag1

if(strcmp(flag,"r") == 0)

{

flag1 = O_RDONLY;

}

// 写方式打开文件,设置flag1 和 iscreate

else if(strcmp(flag,"w") == 0)

{

flag1 = (O_WRONLY | O_CREAT | O_TRUNC);

iscreate = 1;

}

// 追加方式打开文件

else if(strcmp(flag,"a") == 0)

{

flag1 = (O_WRONLY | O_CREAT | O_APPEND);

iscreate = 1;

}

else

{}

int fd = 0;

if(iscreate)

fd = open(path,flag1,mode);// 创建新文件权限限制才有效,传三个参数

else

fd = open(path,flag1);// 打开已经存在的文件不会修改文件权限,使用两个参数即可

if(fd < 0) return NULL;// 打开文件失败返回NULL

// 堆区开辟的空间出了函数不会销毁

myFILE* fp = (myFILE*)malloc(sizeof(myFILE));

if(fp == NULL) return NULL;

fp->fileno = fd;

fp->flags = FLUSH_LINE;// 设置行刷新

fp->cap = LINE_SIZE;

fp->pos = 0;

return fp;

}

1.3、刷新缓冲区

刷新缓冲区实质是将缓冲区内容写入需要刷新的文件中。

void my_fflush(myFILE* fp)

{

// 将缓冲区 pos 个字节的内容写入 fp 的文件中,并将pos置0

write(fp->fileno,fp->cache,fp->pos);

fp->pos = 0;

}

1.4、写文件

写文件的本质是将内容拷贝到缓冲区中,条件允许就刷新缓冲区。

ssize_t my_fwrite(myFILE* fp,const char* data,int len)

{

// 写入的本质是拷贝,条件允许就刷新

memcpy(fp->cache + fp->pos ,data,len);// 需要考虑扩容与越界问题,此处不做处理,从简

fp->pos += len;

// 刷新方式为行刷新且缓冲区遇到\n就刷新缓冲区

if((fp->flags & FLUSH_LINE) && fp->cache[fp->pos-1] == '\n')

{

my_fflush(fp);

}

return len;

}

1.5、关闭文件

先刷新缓冲区,在关闭文件并释放空间。

void my_fclose(myFILE* fp)

{

my_fflush(fp);// 刷新缓冲区

close(fp->fileno);// 关闭文件

free(fp);// 释放空间

}

1.6、各文件代码 

mystdio.h

#pragma once

#include <string.h>

#include <stdlib.h>

#include <sys/types.h>

#include <sys/stat.h>

#include <fcntl.h>

#include <unistd.h>

#define LINE_SIZE 1024

#define FLUSH_NOW 1

#define FLUSH_LINE 2

#define FLUSH_FULL 4

typedef struct _myFILE

{

unsigned int flags;

int fileno;

// 缓冲区

char cache[LINE_SIZE];

int cap;// 容量

int pos;// 下次写入的位置

}myFILE;

myFILE* my_fopen(const char* path,const char* flag);

void my_fflush(myFILE* fp);

ssize_t my_fwrite(myFILE* fp,const char* data,int len);

void my_fclose(myFILE* fp);

mystdio.c

#include "mystdio.h"

myFILE* my_fopen(const char* path,const char* flag)

{

int flag1 = 0;

int iscreate = 0;

mode_t mode = 0666;

if(strcmp(flag,"r") == 0)

{

flag1 = O_RDONLY;

}

else if(strcmp(flag,"w") == 0)

{

flag1 = (O_WRONLY | O_CREAT | O_TRUNC);

iscreate = 1;

}

else if(strcmp(flag,"a") == 0)

{

flag1 = (O_WRONLY | O_CREAT | O_APPEND);

iscreate = 1;

}

else

{}

int fd = 0;

if(iscreate)

fd = open(path,flag1,mode);

else

fd = open(path,flag1);

if(fd < 0) return NULL;

myFILE* fp = (myFILE*)malloc(sizeof(myFILE));

if(fp == NULL) return NULL;

fp->fileno = fd;

fp->flags = FLUSH_LINE;

fp->cap = LINE_SIZE;

fp->pos = 0;

return fp;

}

void my_fflush(myFILE* fp)

{

write(fp->fileno,fp->cache,fp->pos);

fp->pos = 0;

}

ssize_t my_fwrite(myFILE* fp,const char* data,int len)

{

// 写入的本质是拷贝,条件允许就刷新

memcpy(fp->cache + fp->pos ,data,len);// 考虑扩容与越界问题

fp->pos += len;

if((fp->flags&FLUSH_LINE) && fp->cache[fp->pos-1] == '\n')

{

my_fflush(fp);

}

return len;

}

void my_fclose(myFILE* fp)

{

my_fflush(fp);

close(fp->fileno);

free(fp);

}

testfile.c

#include "mystdio.h"

#include <stdio.h>

#define FILENAME "log.txt"

int main()

{

// 使用自己封装的函数以写方式打开文件

myFILE* fp = my_fopen(FILENAME,"w");

if(fp == NULL) return 1;

const char* str = "hello linux";

int cnt = 10;

char buff[128];

while(cnt)

{

// 将格式化数据转成字符串到buff中

sprintf(buff,"%s - %d",str,cnt);

// 将buff写入文件

my_fwrite(fp,buff,strlen(buff));

cnt--;

sleep(1);

my_fflush(fp);// 写完一组数据就刷新缓冲区

}

my_fclose(fp);// 关闭文件

return 0;

}

运行结果

2、stderr

2.1、C语言代码演示

<code>#include <stdio.h>

int main()

{

perror("error:");

fprintf(stdout,"hello fprintf stdout\n");

fprintf(stderr,"hello fprintf stderr\n");

return 0;

}

看现象

我们可以看到一部分数据存入了文件中,但是一部分数据没有存入文件中。

实质是 > 是标准输出重定向,修改1号fd里面的内容,其余的是2号fd里面的内容,因此直接打印到显示器上。 

为什么有了标准输出流还要有标准错误流呢???

因为我们在编写程序的时候不能保证一直都是正确的代码,当我们查错误的时候就可以通过标准错误流查询。标准输出流‌主要用于程序的正常输出信息,标准错误流‌则用于输出程序的错误信息,两者共同确保了程序的运行状态可以被正确地监控和理解。

如果想让2和1都重定向到文件中怎么做? 

1、将数据存入不同的文件

将1号的内容重定向到ok.txt 文件,将2号的内容存入err.txt文件。

命令行代码

<code>[jkl@host file3]$ ./a.out

error:: Success

hello fprintf stdout

hello fprintf stderr

[jkl@host file3]$ ./a.out 1>ok.txt 2>err.txt

[jkl@host file3]$ cat ok.txt

hello fprintf stdout

[jkl@host file3]$ cat err.txt

error:: Success

hello fprintf stderr

运行结果 

2、将数据存入同一个文件 

命令行代码

<code>[jkl@host file3]$ ./a.out 1>all.txt 2>&1

[jkl@host file3]$ cat all.txt

error:: Success

hello fprintf stderr

hello fprintf stdout

运行结果 

总结 

perror("open");本质是向2号打印。printf("");本质是向1号打印。

2.2、C++代码演示

<code>#include <iostream>

int main()

{

std::cout<<"hello cout"<<std::endl;

std::cerr<<"hello cerr"<<std::endl;

return 0;

}

命令行代码 

[jkl@host file3]$ g++ test_stderr.cpp

[jkl@host file3]$ ./a.out

hello cout

hello cerr

[jkl@host file3]$ ./a.out > log.txt

hello cerr

[jkl@host file3]$ cat log.txt

hello cout

运行结果 



声明

本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。