当前位置: 首页 > news >正文

C++项目实战——基于多设计模式下的同步异步日志系统-⑦-日志输出格式化类设计

文章目录

  • 专栏导读
  • 日志格式化类成员介绍
    • pattern
    • items
  • 格式化子项类的设计
      • 抽象格式化子项基类
      • 日志主体消息子项
      • 日志等级子项
      • 时间子项
        • localtime_r介绍
        • strftime介绍
      • 源码文件名子项
      • 源码文件行号子项
      • 线程ID子项
      • 日志器名称子项
      • 制表符子项
      • 换行符子项
      • 原始字符串子项
  • 日志格式化类的设计
    • 设计思想
    • 接口实现
      • Formatter
      • format
      • parsePattern
      • createItem
  • 日志输出格式化类整理

专栏导读

🌸作者简介:花想云 ,在读本科生一枚,C/C++领域新星创作者,新星计划导师,阿里云专家博主,CSDN内容合伙人…致力于 C/C++、Linux 学习。

🌸专栏简介:本文收录于 C++项目——基于多设计模式下的同步与异步日志系统

🌸相关专栏推荐:C语言初阶系列C语言进阶系列C++系列数据结构与算法Linux

在这里插入图片描述

日志格式化类成员介绍

日志消息格式化类主要负责将日志消息进行格式化。类中包含以下成员:

pattern

  • pattern:保存格式化规则字符串
    • 默认日志输出格式[%d{%H:%M:%S}][%t][%c][%f:%l][%p]%T%m%n
    • %d 表示日期,包含子格式 {%H:%M:%S}
    • %T 表示缩进;
    • %t 表示线程ID;
    • %c 表示日志器名称;
    • %f 表示源码文件名;
    • %l 表示源码行号;
    • %p 表示日志级别;
    • %m 表示主体消息;
    • %n 表示换行;

格式化规则字符串控制了日志的输出格式。定义格式化字符,就是为了方便让用户自己决定以何种形式将日志消息进行输出。

例如,在默认输出格式下,输出的日志消息为:
在这里插入图片描述

items

  • std::vector< FormatItem::ptr > items:用于按序保存格式化字符串对应的格式化子项对象。
    • MsgFormatItem:表示要从LogMsg中取出有效载荷;
    • LevelFormatItem:表示要从LogMsg中取出日志等级;
    • LoggerFormatItem:表示要从LogMsg中取出日志器名称;
    • ThreadFormatItem:表示要从LogMsg中取出线程ID;
    • TimeFormatItem:表示要从LogMsg中取出时间戳并按照指定格式进行格式化;
    • FileFormatItem:表示要从LogMsg中取出源码所在文件名;
    • LineFormatItem:表示要从LogMsg中取出源码所在行号;
    • TabFormatItem:表示一个制表符缩进;
    • NLineFormatItem:表示一个换行;
    • OtherFormatItem:表示非格式化的原始字符串;

一个日志消息对象包含许多元素,如时间、线程ID、文件名等等,我们针对不同的元素设计不同的格式化子项。

换句话说,不同的格式化子项从日志消息对象中提取出指定元素,转换为规则字符串并按顺序保存在一块内存空间中。

/*%d 表示日期,包含子格式 {%H:%M:%S}%t 表示线程ID%c 表示日志器名称%f 表示源码文件名%l 表示源码行号%p 表示日志级别%m 表示主体消息%n 表示换行
*/
class Formatter
{
public:// ...
private:std::string _pattern; // 格式化规则字符串std::vector<FormatItem::ptr> _items;
};

格式化子项类的设计

刚才提到对于一条日志消息message,其中包含很多元素(时间、线程ID等)。我们通过设计不同的格式化子项来取出指定的元素,并将它们追加到一块内存空间中。

但是由于不同的格式化子项类对象类型也各不相同,我们就采用多态的思想,抽象出一个格式化子项基类,基于基类派生出不同的格式化子项类。这样就可以定义父类指针的数组,指向不同的格式化子项子类对象。

抽象格式化子项基类

class FormatItem
{
public:using ptr = std::shared_ptr<FormatItem>;virtual void format(std::ostream &out, const LogMsg &msg) = 0;
};
  • 在基类中定义一个智能指针对象,方便管理;
  • format的参数为一个IO流对象,一个LogMsg对象。作用为提取LogMsg对象中的指定元素追加到流对象中。

日志主体消息子项

class MsgFormatItem : public FormatItem
{
public:void format(std::ostream &out, const LogMsg &msg) override{out << msg._payload;}
};

日志等级子项

class MsgFormatItem : public FormatItem
{
public:void format(std::ostream &out, const LogMsg &msg) override{out << msg._payload;}
};

时间子项

class TimeFormatItem : public FormatItem
{
public:TimeFormatItem(const std::string &fmt = "%H:%M:%S") : _time_fmt(fmt) {}void format(std::ostream &out, const LogMsg &msg) override{struct tm t;localtime_r(&msg._ctime, &t);char tmp[32] = {0};strftime(tmp, 31, _time_fmt.c_str(), &t);out << tmp;}private:std::string _time_fmt; // %H:%M:%S
};
  • 时间子项可以设置子格式,在构造函数中需要传递一个子格式字符串来控制时间子格式;

localtime_r介绍

在LogMsg对象中,时间元素是一个时间戳数字,不方便观察时间信息。我们需要将该时间戳转化为易于观察的时分秒的格式。

localtime_r函数是C标准库中的一个函数,用于将时间戳(表示自1970年1月1日以来的秒数)转换为本地时间的表示。这个函数是线程安全的版本,它接受两个参数:一个指向时间戳的指针和一个指向struct tm类型的指针,它会将转换后的本地时间信息存储在struct tm结构中。

函数原型如下:

struct tm *localtime_r(const time_t *timep, struct tm *result);
  • timep参数是指向时间戳的指针;
  • result参数是指向struct tm类型的指针,用于存储转换后的本地时间信息;
  • localtime_r函数返回一个指向struct tm结构的指针,同时也将结果存储在result参数中;
  • struct tm结构包含了年、月、日、时、分、秒等时间信息的成员变量,可用于格式化和输出时间;

struct tm类型
struct tm是C语言中的一个结构体类型,用于表示日期和时间的各个组成部分。

struct tm结构包含以下成员变量:

struct tm
{int tm_sec; // 秒(0-59)int tm_min; // 分钟(0-59)int tm_hour; // 小时(0-23)int tm_mday; // 一个月中的日期(1-31)int tm_mon; // 月份(0-11,0代表1月)int tm_year; // 年份(从1900年起的年数,例如,121表示2021年)int tm_wday; // 一周中的星期几(0-6,0代表星期日)int tm_yday; // 一年中的第几天(0-365)int tm_isdst; // 是否为夏令时(正数表示是夏令时,0表示不是,负数表示夏令时信息不可用)
}

strftime介绍

strftime函数是C标准库中的一个函数,用于将日期和时间按照指定的格式进行格式化,并将结果存储到一个字符数组中。这个函数在C语言中非常常用,特别是在需要将日期和时间以不同的格式输出到屏幕、文件或其他输出设备时。

函数原型如下:

size_t strftime(char *s, size_t maxsize, const char *format, const struct tm *timeptr);
  • s:一个指向字符数组的指针,用于存储格式化后的日期和时间字符串;
  • maxsize:指定了字符数组 s 的最大容量,以防止缓冲区溢出;
  • format:一个字符串,用于指定日期和时间的输出格式。该字符串可以包含- 各种格式化控制符,例如%Y表示年份,%m表示月份等等;
  • timeptr:一个指向struct tm 结构的指针,表示待格式化的日期和时间;

返回值:

  • strftime函数返回生成的字符数(不包括空终止符\0),如果生成的字符数大于 maxsize,则返回0,表示字符串无法完全存储在给定的缓冲区中。

源码文件名子项

class FileFormatItem : public FormatItem
{
public:void format(std::ostream &out, const LogMsg &msg) override{out << msg._file;}
};

源码文件行号子项

class LineFormatItem : public FormatItem
{
public:void format(std::ostream &out, const LogMsg &msg) override{out << msg._line;}
};

线程ID子项

class ThreadFormatItem : public FormatItem
{
public:void format(std::ostream &out, const LogMsg &msg) override{out << msg._tid;}
};

日志器名称子项

class LoggerFormatItem : public FormatItem
{
public:void format(std::ostream &out, const LogMsg &msg) override{out << msg._logger;}
};

制表符子项

class TabFormatItem : public FormatItem
{
public:void format(std::ostream &out, const LogMsg &msg) override{out << "\t";}
};

换行符子项

class NLineFormatItem : public FormatItem
{
public:void format(std::ostream &out, const LogMsg &msg) override{out << "\n";}
};

原始字符串子项

class OtherFormatItem : public FormatItem
{
public:OtherFormatItem(const std::string &str) : _str(str) {}void format(std::ostream &out, const LogMsg &msg) override{out << _str;}private:std::string _str;
};

解释一下原始字符串:

  • 例如一个格式化字符串为[%d{%H:%M:%S}][%t][%c][%f:%l][%p]%T%m%n,其中[]:等符号是不属于上述任何子项的,这些符号不需要被解析,它们会作为日志内容被直接输出。

日志格式化类的设计

设计思想

日志格式化Formatter类中提供四个接口:

class Formatter
{
public:using ptr = std::shared_ptr<Formatter>;Formatter(const std::string &pattern = "[%d{%H:%M:%S}][%t][%c][%f:%l][%p]%T%m%n");void format(std::ostream &out, const LogMsg &msg);std::string format(const LogMsg &msg);
private:bool parsePattern();// 根据不同的格式化字符创建不同得格式化子项对象FormatItem::ptr createItem(const std::string &key, const std::string &val);
private:std::string _pattern; // 格式化规则字符串std::vector<FormatItem::ptr> _items;
};
  • Formatter:构造函数,构造一个formatter对象。函数参数为一个格式化字符串用来初始化成员pattern
  • format:提供两个重载函数,函数作用为将LogMsg中元素提取出来交由对应的格式化子项处理;可以将LogMsg进行格式化,并追加到流对象当中,也可以直接返回格式化后的字符串;
  • parsePattern:用于解析规则字符串_pattern
  • createItem:用于根据不同的格式化字符串创建不同的格式化子项对象

接口实现

Formatter

// 时间{年-月-日 时:分:秒}缩进 线程ID 缩进 [日志级别] 缩进 [日志名称] 缩进 文件名:行号 缩进 消息换行
Formatter(const std::string &pattern = "[%d{%H:%M:%S}][%t][%c][%f:%l][%p]%T%m%n"): _pattern(pattern)
{assert(parsePattern()); // 确保格式化字符串有效
}

format

// 对msg进行格式化
void format(std::ostream &out, const LogMsg &msg)
{for (auto &item : _items){item->format(out, msg);}
}std::string format(const LogMsg &msg)
{std::stringstream ss;format(ss, msg);return ss.str();
}

parsePattern

函数设计思想

  • 函数的主要逻辑是从前往后的处理格式化字符串。以默认格式化字符串"[%d{%H:%M:%S}][%t][%c][%f:%l][%p]%T%m%n"为例:
    • 从前往后遍历,如果没有遇到%则说明之前的字符都是原始字符串;
    • 遇到%,则看紧随其后的是不是另一个%,如果是,则认为%就是原始字符串;
    • 如果%后面紧挨着的是格式化字符(c、f、l、S等),则进行处理;
    • 紧随格式化字符之后,如果有{,则认为在{之后、}之前都是子格式内容;

在处理过程中,我们需要将得到的结果保存下来,于是我们可以创建一个vector,类型为一个键值对(key,val)。如果是格式化字符,则key为该格式化字符valnull;若为原始字符串keynullval原始字符串内容

得到数组之后,根据数组内容,调用createItem函数创建对应的格式化子项对象,添加到items成员中。

bool parsePattern()
{std::vector<std::pair<std::string, std::string>> fmt_order;size_t pos = 0;std::string key, val;while (pos < _pattern.size()){if (_pattern[pos] != '%'){val.push_back(_pattern[pos++]);continue;}if (pos + 1 < _pattern.size() && _pattern[pos + 1] == '%'){val.push_back('%');pos += 2;continue;}if (val.empty() == false){fmt_order.push_back(std::make_pair("", val));val.clear();}pos += 1;if (pos == _pattern.size()){std::cout << "%之后没有格式化字符\n";return false;}key = _pattern[pos];pos += 1;if (pos < _pattern.size() && _pattern[pos] == '{'){pos += 1;while (pos < _pattern.size() && _pattern[pos] != '}'){val.push_back(_pattern[pos++]);}if (pos == _pattern.size()){std::cout << "子规则{}匹配出错\n";return false;}pos += 1;}fmt_order.push_back(std::make_pair(key, val));key.clear();val.clear();}for (auto &it : fmt_order){_items.push_back(createItem(it.first, it.second));}return true;
}

createItem

  • 根据不同的格式化字符创建不同得格式化子项对象;
// 根据不同的格式化字符创建不同得格式化子项对象
FormatItem::ptr createItem(const std::string &key, const std::string &val)
{if (key == "d")return std::make_shared<TimeFormatItem>(val);if (key == "t")return std::make_shared<ThreadFormatItem>();if (key == "c")return std::make_shared<LoggerFormatItem>();if (key == "f")return std::make_shared<FileFormatItem>();if (key == "l")return std::make_shared<LineFormatItem>();if (key == "p")return std::make_shared<LevelFormatItem>();if (key == "T")return std::make_shared<TabFormatItem>();if (key == "m")return std::make_shared<MsgFormatItem>();if (key == "n")return std::make_shared<NLineFormatItem>();if (key == "")return std::make_shared<OtherFormatItem>(val);std::cout << "没有对应的格式化字符串:%" << key << std::endl;abort();
}

至此,日志消息格式化类已经全部实现完毕。


日志输出格式化类整理

#ifndef __M_FMT_H__
#define __M_FMT_H__#include "level.hpp"
#include "message.hpp"
#include <vector>
#include <sstream>
#include <ctime>
#include <cassert>namespace LOG
{// 抽象格式化子项基类class FormatItem{public:using ptr = std::shared_ptr<FormatItem>;virtual void format(std::ostream &out, const LogMsg &msg) = 0;};class MsgFormatItem : public FormatItem{public:void format(std::ostream &out, const LogMsg &msg) override{out << msg._payload;}};class LevelFormatItem : public FormatItem{public:void format(std::ostream &out, const LogMsg &msg) override{out << LogLevel::tostring(msg._level);}};class TimeFormatItem : public FormatItem{public:TimeFormatItem(const std::string &fmt = "%H:%M:%S") : _time_fmt(fmt) {}void format(std::ostream &out, const LogMsg &msg) override{struct tm t;localtime_r(&msg._ctime, &t);char tmp[32] = {0};strftime(tmp, 31, _time_fmt.c_str(), &t);out << tmp;}private:std::string _time_fmt; // %H:%M:%S};class FileFormatItem : public FormatItem{public:void format(std::ostream &out, const LogMsg &msg) override{out << msg._file;}};class LineFormatItem : public FormatItem{public:void format(std::ostream &out, const LogMsg &msg) override{out << msg._line;}};class ThreadFormatItem : public FormatItem{public:void format(std::ostream &out, const LogMsg &msg) override{out << msg._tid;}};class LoggerFormatItem : public FormatItem{public:void format(std::ostream &out, const LogMsg &msg) override{out << msg._logger;}};class TabFormatItem : public FormatItem{public:void format(std::ostream &out, const LogMsg &msg) override{out << "\t";}};class NLineFormatItem : public FormatItem{public:void format(std::ostream &out, const LogMsg &msg) override{out << "\n";}};class OtherFormatItem : public FormatItem{public:OtherFormatItem(const std::string &str) : _str(str) {}void format(std::ostream &out, const LogMsg &msg) override{out << _str;}private:std::string _str;};/*%d 表示日期,包含子格式 {%H:%M:%S}%t 表示线程ID%c 表示日志器名称%f 表示源码文件名%l 表示源码行号%p 表示日志级别%m 表示主体消息%n 表示换行*/class Formatter{public:using ptr = std::shared_ptr<Formatter>;// 时间{年-月-日 时:分:秒}缩进 线程ID 缩进 [日志级别] 缩进 [日志名称] 缩进 文件名:行号 缩进 消息换行Formatter(const std::string &pattern = "[%d{%H:%M:%S}][%t][%c][%f:%l][%p]%T%m%n"): _pattern(pattern){assert(parsePattern());}// 对msg进行格式化void format(std::ostream &out, const LogMsg &msg){for (auto &item : _items){item->format(out, msg);}}std::string format(const LogMsg &msg){std::stringstream ss;format(ss, msg);return ss.str();}private:// 解析格式化字符串bool parsePattern(){std::vector<std::pair<std::string, std::string>> fmt_order;size_t pos = 0;std::string key, val;while (pos < _pattern.size()){if (_pattern[pos] != '%'){val.push_back(_pattern[pos++]);continue;}if (pos + 1 < _pattern.size() && _pattern[pos + 1] == '%'){val.push_back('%');pos += 2;continue;}if (val.empty() == false){fmt_order.push_back(std::make_pair("", val));val.clear();}pos += 1;if (pos == _pattern.size()){std::cout << "%之后没有格式化字符\n";return false;}key = _pattern[pos];pos += 1;if (pos < _pattern.size() && _pattern[pos] == '{'){pos += 1;while (pos < _pattern.size() && _pattern[pos] != '}'){val.push_back(_pattern[pos++]);}if (pos == _pattern.size()){std::cout << "子规则{}匹配出错\n";return false;}pos += 1;}fmt_order.push_back(std::make_pair(key, val));key.clear();val.clear();}for (auto &it : fmt_order){_items.push_back(createItem(it.first, it.second));}return true;}// 根据不同的格式化字符创建不同得格式化子项对象FormatItem::ptr createItem(const std::string &key, const std::string &val){if (key == "d")return std::make_shared<TimeFormatItem>(val);if (key == "t")return std::make_shared<ThreadFormatItem>();if (key == "c")return std::make_shared<LoggerFormatItem>();if (key == "f")return std::make_shared<FileFormatItem>();if (key == "l")return std::make_shared<LineFormatItem>();if (key == "p")return std::make_shared<LevelFormatItem>();if (key == "T")return std::make_shared<TabFormatItem>();if (key == "m")return std::make_shared<MsgFormatItem>();if (key == "n")return std::make_shared<NLineFormatItem>();if (key == "")return std::make_shared<OtherFormatItem>(val);std::cout << "没有对应的格式化字符串:%" << key << std::endl;abort();}private:std::string _pattern; // 格式化规则字符串std::vector<FormatItem::ptr> _items;};
}#endif

在这里插入图片描述

相关文章:

C++项目实战——基于多设计模式下的同步异步日志系统-⑦-日志输出格式化类设计

文章目录 专栏导读日志格式化类成员介绍patternitems 格式化子项类的设计抽象格式化子项基类日志主体消息子项日志等级子项时间子项localtime_r介绍strftime介绍 源码文件名子项源码文件行号子项线程ID子项日志器名称子项制表符子项换行符子项原始字符串子项 日志格式化类的设计…...

Android---底部弹窗之BottomSheetDialog

BottomSheetDialog 是Android开发中的一个弹出式对话框&#xff0c;它从屏幕底部弹出并覆盖部分主界面。 1. BottomSheetDialog的使用 // 参数2&#xff1a;设置BottomSheetDialog的主题样式&#xff1b;将背景设置为transparent&#xff0c;这样我们写的shape_bottom_sheet_…...

Cesium 地球网格构造

Cesium 地球网格构造 Cesium原理篇&#xff1a;3最长的一帧之地形(2&#xff1a;高度图) HeightmapTessellator 用于从高程图像创建网格。提供了一个函数 computeVertices&#xff0c;可以根据高程图像创建顶点数组。 该函数的参数包括高程图像、高度数据的结构、网格宽高、…...

C++深度优化——cacheline测试

cacheline是内存调度的基本结构&#xff0c;其大小一般为32B或者64B。关于本机具体的配置信息可以在配置文件中看到&#xff1a; 这里可以看到我的这台机器的cacheline大小是64B。对于cacheline在多核处理器中有一个伪共享的状态&#xff0c;具体可以参考以下博客&#xff1a;高…...

【数字IC/FPGA】Verilog中的递归调用

参考文章 在Verilog2001中,模块的递归调用是可能的,引用下面的一段话(出自上面的参考文章) Many designers think that recursive techniques cannot be applied to hardware design. I’m not really sure where this misconception comes from. While it is true that i…...

禁用Win10自动更新

第一步&#xff0c;winr&#xff0c;输入 gpedit.msc 并回车&#xff0c;打开【组策略】 第二步&#xff0c;依次点击 管理模板->Windows组件->Windows更新 第三步&#xff0c;双击Windows更新&#xff0c;然后在设置中双击 指定 intranet Microsoft 更新服务位置 第…...

算法通关村-----动态规划高频问题

最少硬币数问题 问题描述 给你一个整数数组 coins &#xff0c;表示不同面额的硬币&#xff1b;以及一个整数 amount &#xff0c;表示总金额。计算并返回可以凑成总金额所需的 最少的硬币个数 。如果没有任何一种硬币组合能组成总金额&#xff0c;返回 -1 。你可以认为每种硬…...

记一起小意外事件引起的批量重命名文件名

一、事件描述 某次,因某业务系统迁移,一线人员对业务目录误操作,执行打包命令过程中导致Tomcat下的web应用程序无法使用,检查后发现项目下所有文件名都加了gz格式;询问一线,发现是对项目目录执行了:gzip -r ./tomcat导致程序文件找不到;报错如下: 二、事件处理 1、查看…...

【Excel函数】Excel的Len函数求对象的字符数

在Excel中&#xff0c;LEN函数用于计算文本字符串中的字符数。它的语法如下。 LEN(text) 其中&#xff0c;text是要计算字符数的文本字符串。 例如&#xff0c;如果你想计算单元格A1中文本的字符数&#xff0c;可以使用以下公式&#xff1a; A2len(a1) 结果将返回单元格A1中文…...

小白备战大厂算法笔试(八)——搜索

搜索 二分查找 二分查找是一种基于分治策略的高效搜索算法。它利用数据的有序性&#xff0c;每轮减少一半搜索范围&#xff0c;直至找到目标元素或搜索区间为空为止。 Question&#xff1a; 给定一个长度为n的数组 nums &#xff0c;元素按从小到大的顺序排列&#xff0c;数组…...

〔022〕Stable Diffusion 之 生成视频 篇

✨ 目录 &#x1f388; 视频转换 / mov2mov&#x1f388; 视频转换前奏准备&#x1f388; 视频转换 mov2mov 使用&#x1f388; 视频转换 mov2mov 效果预览&#x1f388; 视频无限缩放 / Infinite Zoom&#x1f388; 视频无限缩放 Infinite Zoom 使用 &#x1f388; 视频转换 /…...

网络安全深入学习第三课——热门框架漏洞(RCE—Struts2远程代码执行)

文章目录 一、Struts2框架介绍二、Struts2远程代码执行漏洞三、Struts2执行代码的原理四、Struts2框架特征五、漏洞手工POC六、漏洞工具复现 一、Struts2框架介绍 ------ Struts2是apache项目下的一个web 框架&#xff0c;普遍应用于阿里巴巴、京东等互联网、政府、企业门户网…...

【uni-app】

准备工作&#xff08;Hbuilder&#xff09; 1.下载hbuilder&#xff0c;插件使用Vue3的uni-app项目 2.需要安装编译器 3.下载微信开发者工具 4.点击运行->微信开发者工具 5.打开微信开发者工具的服务端口 效果图 准备工作&#xff08;VScode&#xff09; 插件 uni-cr…...

Pytorch 多卡并行(3)—— 使用 DDP 加速 minGPT 训练

前文 并行原理简介和 DDP 并行实践 和 使用 torchrun 进行容错处理 在简单的随机数据上演示了使用 DDP 并行加速训练的方法&#xff0c;本文考虑一个更加复杂的 GPT 类模型&#xff0c;说明如何进行 DDP 并行实战MinGPT 是 GPT 模型的一个流行的开源 PyTorch 复现项目&#xff…...

IAM、EIAM、CIAM、RAM、IDaaS 都是什么?

后端程序员在做 ToB 产品或者后台系统时&#xff0c;都不可避免的会遇到账号系统、登录系统、权限系统、日志系统等这些核心功能。这些功能一般都是以 SSO 系统、RBAC 权限管理系统等方式命名&#xff0c;但这些系统合起来有一个专有名词&#xff1a;IAM。 IAM IAM 是 Identi…...

STM32 Cubemx 通用定时器 General-Purpose Timers同步

文章目录 前言简介cubemx配置 前言 持续学习stm32中… 简介 通用定时器是一个16位的计数器&#xff0c;支持向上up、向下down与中心对称up-down三种模式。可以用于测量信号脉宽&#xff08;输入捕捉&#xff09;&#xff0c;输出一定的波形&#xff08;比较输出与PWM输出&am…...

Ubuntu 20.04降级clang-format

1. 卸载clang-format sudo apt purge clang-format 2. 安装clang-format-6.0 sudo apt install clang-format-6.0 3. 软链接clang-format sudo ln -s /usr/bin/clang-format-6.0 /usr/bin/clang-format...

激活函数总结(三十四):激活函数补充(FReLU、CReLU)

激活函数总结&#xff08;三十四&#xff09;&#xff1a;激活函数补充 1 引言2 激活函数2.1 FReLU激活函数2.2 CReLU激活函数 3. 总结 1 引言 在前面的文章中已经介绍了介绍了一系列激活函数 (Sigmoid、Tanh、ReLU、Leaky ReLU、PReLU、Swish、ELU、SELU、GELU、Softmax、Sof…...

【LeetCode-简单题KMP】459. 重复的子字符串

文章目录 题目方法一&#xff1a;移动匹配方法二&#xff1a;KMP算法 题目 方法一&#xff1a;移动匹配 class Solution {//移动匹配public boolean repeatedSubstringPattern(String s) {StringBuffer str new StringBuffer(s);//ababstr.append(s);//拼接一份自己 abababab…...

Lua脚本

基本语法 注释 print(“script lua win”) – 单行注释 – [[ 多行注释 ]] – 标识符 类似于&#xff1a;java当中 变量、属性名、方法名。 以字母&#xff08;a-z,A-Z&#xff09;、下划线 开头&#xff0c;后面加上0个或多个 字母、下划线、数字。 不要用下划线大写字母…...

vue 封装一个Dialog组件

基于element-plus封装一个Dialog组件 <template><section class"dialog-wrap"><el-dialog :title"title" v-model"visible" :close-on-click-modal"false"><section class"content-wrap"><Form…...

外包干了2个月,技术退步明显。。。。。

先说一下自己的情况&#xff0c;大专生&#xff0c;18年通过校招进入武汉某软件公司&#xff0c;干了接近4年的功能测试&#xff0c;今年年初&#xff0c;感觉自己不能够在这样下去了&#xff0c;长时间呆在一个舒适的环境会让一个人堕落!而我已经在一个企业干了四年的功能测试…...

python科研作图

1、气泡图 气泡图是一种在xy轴上显示三个维度的数据的有效方式。在气泡图中&#xff0c;基本上&#xff0c;每个气泡代表一个数据点。横坐标和纵坐标的位置代表两个维度&#xff0c;气泡的大小则代表第三个维度。 在这个例子中&#xff0c;我们用numpy库生成了一些随机数据&a…...

视锥体裁剪射线的算法

射线Ray(直线情况)需要满足的条件: 在视野中显示的粗细均匀,需要分段绘制,每段的粗细根据到视野的距离计算射线model的顶点尽量少以节省性能损耗要满足条件2的话需要对射线进行裁剪,只绘制射线在视锥体内的部分,因此需要计算射线被视锥体裁剪后新的起点和终点 1. 计算三角…...

程序员在线周刊(投稿篇)

嗨&#xff0c;大家好&#xff01;作为一名程序员&#xff0c;并且是一名互联网文章作者&#xff0c;我非常激动地向大家宣布&#xff0c;我们计划推出一份在线周刊&#xff0c;专门为程序员们提供有趣、实用的文章和资讯。而现在&#xff0c;我们正在征集投稿&#xff01; 是…...

uniapp——实现聊天室功能——技能提升

这里写目录标题 效果图聊天室功能代码——html部分代码——js部分代码——其他部分 首先声明一点&#xff1a;下面的内容是从一个uniapp的程序中摘录的&#xff0c;并非本人所写&#xff0c;先做记录&#xff0c;以免后续遇到相似需求抓耳挠腮。 效果图 聊天室功能 发送图片 …...

脚本:用python实现五子棋

文章目录 1. 语言2. 效果3. 脚本4. 解读5. FutureReference 1. 语言 Python 无环境配置、无库安装。 2. 效果 以第一回合为例 玩家X 玩家0 3. 脚本 class GomokuGame:def __init__(self, board_size15):self.board_size board_sizeself.board [[ for _ in range(board_…...

Java-华为真题-预定酒店

需求&#xff1a; 放暑假了&#xff0c;小王决定到某旅游景点游玩&#xff0c;他在网上搜索到了各种价位的酒店&#xff08;长度为n的数组A&#xff09;&#xff0c;他的心理价位是x元&#xff0c;请帮他筛选出k个最接近x元的酒店&#xff08;n>k>0&#xff09;&#xff…...

win10 自带虚拟机软件 虚拟CentOS系统

win10 下使用需要虚拟一个系统&#xff0c;不需要额外安装VMware、Virtual box等软件。使用win10 自带虚拟机软件即可 步骤1 确保启动Hyper-V 功能启用 控制面板 -> 程序 -> 启用或关闭Windows功能 步骤 2 创建虚拟机 2.1 打开Typer-V 2.2 创建虚拟机 2.2.1 操作 -&g…...

【深度学习】 Python 和 NumPy 系列教程(十):NumPy详解:2、数组操作(索引和切片、形状操作、转置操作、拼接操作)

目录 一、前言 二、实验环境 三、NumPy 0、多维数组对象&#xff08;ndarray&#xff09; 1. 多维数组的属性 1、创建数组 2、数组操作 1. 索引和切片 a. 索引 b. 切片 2. 形状操作 a. 获取数组形状 b. 改变数组形状 c. 展平数组 3. 转置操作 a. 使用.T属性 b…...

艺友网站建设/北京官网优化公司

降级策略RT是平均响应时间策略 设置RT的响应时间单位毫秒&#xff0c;RT最大值为4900毫秒&#xff0c;需要变更此上限可以通过启动配置项 -Dcsp.sentinel.statistic.max.rtxxx来配置 需要使用到jmeter测试工具&#xff0c;jmeter下载页面 下载zip包即可 解压后修改配置文件b…...

响应式外贸网站建设/沈阳seo合作

作者&#xff1a;桂。 时间&#xff1a;2017-03-27 20:26:17 链接&#xff1a;http://www.cnblogs.com/xingshansi/p/6628785.html 声明&#xff1a;欢迎被转载&#xff0c;不过记得注明出处哦~ 【读书笔记06】 前言 看到西蒙.赫金的《自适应滤波器原理》第四版第四章&#xf…...

做网红用哪个网站/必应搜索引擎网站

16年元旦顶着大风来北京面试&#xff0c;一天6轮&#xff0c;在回去的高铁上感觉面试可能挂了&#xff0c;一个礼拜后收到了Offer。由于自身各方面的原因&#xff0c;18年元旦&#xff0c;选择离开微软。It is hard to say LEAVE, 这里整理了一下我在微软的两年经历&#xff0c…...

成品超市网站/seo排名查询软件

我们打开昨天已经创建好的项目myself.pro.注意保存的项目路径不能含有中文.接下来我们继续来学习,看如何进行可视化编程. 双点你的工程管理窗口中界面文件mainwindow.ui.双点后如图所示. 控件组窗口包含所有控件(控件就是一个叫法而已,例如按钮,文本框,标签等都被称为控件),使…...

欧美设计网站/优化电池充电什么意思

这样的需求不在少数&#xff0c;比如表格中相邻的列具有相同的内容&#xff0c;那么标题就完全可以使用一个&#xff0c;那么合并标题就是十分重要的&#xff0c;让用户感觉也会更加人性化&#xff0c;代码实例如下: <!DOCTYPE html> <html> <head> <met…...

php网站培训机构企业做网站/seo成功案例分析

本篇文章帮大家学习xml-RPC实例(java)&#xff0c;包含了XML-RPC实例(Java)使用方法、操作技巧、实例演示和注意事项&#xff0c;有一定的学习价值&#xff0c;大家可以用来参考。在本节中&#xff0c;将通过Java编程语言演示如何使用XML-RPC&#xff0c;首先创建一个使用Java类…...