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

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

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

💗系列专栏: 【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、定义文件结构

#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语言代码演示

#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文件。

命令行代码

[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、将数据存入同一个文件 

命令行代码

[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++代码演示

#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 

运行结果 

Read more

优选算法《滑动窗口》

优选算法《滑动窗口》

在优选算法的第一章当中我们了解了双指针算法,相信通过那几道算法题的讲解你已经知道该如何灵活的使用双指针了吧,那么接下来我们就接着来学习下一个优选算法——滑动窗口,你这时可能会疑惑这个算法在之前怎么完全没有听说过,没有关系接下来在本篇当中就将带你一步步的了解滑动窗口的算法原理以及在什么情况下适合使用滑动窗口来解决问题,并且还会通过几道算法题的讲解让你进一步的理解滑动窗口。那么接下来就开始本篇的学习吧!!! 1.滑动窗口算法 在此滑动窗口算法其实就是一种双指针中的特殊情况,只不过是因为在这种情况下的双指针有特殊的规律,因此我们就单独的将这种双指针的情况单独命名为一种算法,那么滑动窗口算法是什么情况下的双指针算法呢?接下来我们就来了解看看 在使用双指针当中,若出现两个指针从开始到结束一直都是朝着同一个方向移动的,并且都不会出现超另一个方向移动的情况这种就叫做滑动窗口,在此叫做这种算法是因为两个指针一直朝着同一个方向移动就像一个大小可能会变化的窗口一样。   那么了解了滑动窗口是什么之后,你可能会有疑惑了那滑动窗口该怎么用并且滑动窗口为什么算法逻辑是正确的呢,接下来我们就通过

By Ne0inhk
算法:基础数论

算法:基础数论

1. 最大公约数和最小公倍数 【约数和倍数】 * 如果 a 除以 b 没有余数,那么 a 就是 b 的倍数,b 就是 a 的约数,记作 b∣a。 * 约数,也称因数。 【最大公约数和最小公倍数】 最大公约数(Greatest Common Divisor,常缩写为 gcd) * 一组整数的公约数,是指同时是这组数中每一个数的约数的数。 * 一组整数的最大公约数,是指所有公约数里面最大的一个。 最小公倍数(Least Common Multiple,常缩写为 lcm) * 一组整数的公倍数,是指同时是这组数中每一个数的倍数的数。 * 一组整数的最小公倍数,是指所有正的公倍数里面,最小的一个数。 求两个数的 gcd 与 lcm 时的性质 * 对于两个数

By Ne0inhk
算法基础篇:(二十一)数据结构之单调栈:从原理到实战,玩转高效解题

算法基础篇:(二十一)数据结构之单调栈:从原理到实战,玩转高效解题

目录 前言 一、什么是单调栈?先打破 “栈” 的常规认知 1.1 单调栈的核心特性 1.2 如何实现一个单调栈? 实现单调递增栈 实现单调递减栈 1.3 核心操作解析:为什么要 “弹出元素”? 二、单调栈能解决什么问题?四大核心场景全覆盖 2.1 场景 1:找左侧最近的 “更大元素” 问题描述 解题思路 代码实现 测试用例验证 2.2 场景 2:找左侧最近的 “更小元素” 问题描述 解题思路 代码实现 测试用例验证 2.3 场景 3:找右侧最近的 “更大元素” 问题描述

By Ne0inhk
【希尔排序算法】详解:原理、实现与优化

【希尔排序算法】详解:原理、实现与优化

【希尔排序算法】详解:原理、实现与优化 * 一、算法概述 * 基本特性 * 二、算法原理详解 * 核心思想 * 增量序列选择 * 三、算法流程图示 * 示例数组:[8, 9, 1, 7, 2, 3, 5, 4, 6, 0] * 初始状态 * 第一轮:gap=5 * 第二轮:gap=2 * 第三轮:gap=1(标准插入排序) * 四、完整Java实现 * 五、算法分析 * 时间复杂度分析 * 空间复杂度 * 稳定性 * 六、实际应用场景 * 七、与其他排序算法的对比 * 八、总结 🌺The Begin�

By Ne0inhk