HtmlRender - c++实现的html生成类

ops/2024/11/11 6:11:10/

HtmlRender

CppTinParser/render.hpp中定义和实现。

使用c++实现的简易Html编辑类。

简介

目前,c++有几个Html解析器,而少见便捷规范的html生成器,HtmlRender则提供了一个简单的、规范的html内容生成器。用c++实现html内容生成器,并不是简单的字符串拼接,这样会导致代码编写不规范、易读性下降,而且无法应对复杂html生成任务。HtmlRender借鉴了python的第三方html编写库——dominate


使用接口

初始化

HtmlRender* item = HtmlRender(string tag, string content, map<string,string> kws, bool onetag=false, bool pre=false)
//tag 标签名称,如果为空字符串,则在生成时直接产生content,不添加标记
//content 标签内容
//kws 标签参数
//onetag 是否为单标签,比如<br>, <hr>等
//pre 是否显示为原文本

html_25">生成html文本

string result = HtmlRender.render()

该函数将生成并返回该HtmlRender所包含的html文本内容。一般应只用tag = "html"HtmlRender使用该函数,以此来实现完整的html文本。当然,任意HtmlRender实例均可,单标签也可使用该函数,但是生成内容将大概率出现错误。

添加子元素

为了方便操作,HtmlRender使用list作为容器来存取子元素指针,这里的子元素同样是HtmlRender。具体增加代码如下:

HtmlRender* subitem = new HtmlRender(...)
item.add(subitem)

如果父元素本身就是通过new创建,则使用如下代码:

item->add(subitem)

设置标签元素内容和参数

设置内容:

item.configcnt(string content)

设置参数:

item.configkws(map<string, string> kws)

获取父元素指针

除了顶级元素,任何一个标签元素在被使用add方法后,都会有明确的父元素。

通过如下代码获取父元素指针:

HtmlRender* p_item = subitem->parent()
//p_item == &item

获取子元素指针列表

如果一个元素使用了add方法,则必然含有子元素。

使用如下代码获取子元素指针列表

list<HtmlRender*> children = item.children()

实现原理

添加子元素

获取子元素指针,在子元素指针列表中添加该指针。

void HtmlRender::add(HtmlRender* item) {//添加html内容this->htmlcontent.push_back(item);item->_parent = this;
}

渲染

首先生成元素自身标签、参数、内容,然后遍历子元素指针列表,获取所有子元素以及多级子元素的渲染生成内容,最后加上自身结束标签(如果有的话)。

string HtmlRender::render() {//渲染为html文本if (this->tag == ""){return this->content;}string htmltext = "<" + this->tag;for (auto &kw : this->kws) {htmltext += " " + kw.first + "=\"" + kw.second + "\"";}htmltext += ">";htmltext += this->content;for (auto item = this->htmlcontent.begin(); item != this->htmlcontent.end(); ++item) {string subtext = (*item)->render();htmltext += "\n" + subtext;}if (this->onetag){//单标签htmltext += "\n";}else{htmltext += "\n</" + this->tag + ">";}return htmltext;
}

示例

测试代码:

int main(){HtmlRender html = HtmlRender("html", "", {});HtmlRender* head = new HtmlRender("head", "", {});HtmlRender* title = new HtmlRender("title", "TinML", {});head->add(title);html.add(head);HtmlRender* body = new HtmlRender("body", "", {});html.add(body);HtmlRender* t1 = new HtmlRender("h1", "TITLE", {});body->add(t1);for (int i=0; i<10; i++){HtmlRender* p = new HtmlRender("p", "paragraph - " + to_string(i), {});body->add(p);}string htmltext = html.render();cout << htmltext <<endl;return 0;
}

结果:

html"><html>
<head>
<title>TinML
</title>
</head>
<body>
<h1>TITLE
</h1>
<p>paragraph - 0
</p>
<p>paragraph - 1
</p>
<p>paragraph - 2
</p>
<p>paragraph - 3
</p>
<p>paragraph - 4
</p>
<p>paragraph - 5
</p>
<p>paragraph - 6
</p>
<p>paragraph - 7
</p>
<p>paragraph - 8
</p>
<p>paragraph - 9
</p>
</body>
</html>

主要源码

string subreplace(std::string resource_str, std::string sub_str, std::string new_str){string dst_str = resource_str;string::size_type pos = -1;//这里要专门处理&转义后仍存在的&符号,所以find使用了poswhile((pos = dst_str.find(sub_str, pos+1)) != std::string::npos)   //替换所有指定子串{dst_str.replace(pos, sub_str.length(), new_str);}return dst_str;
}class HtmlRender {//html编辑类
public:HtmlRender()=default;HtmlRender(string tag, string content, map<string,string> kws, bool onetag=false, bool pre=false){this->tag = tag;// this->content = content;// this->kws = kws;this->pre = pre;if (pre){//原文本内容this->content = content;}else{this->load_content(content);}this->load_kws(kws);this->onetag = onetag;this->_parent = NULL;}HtmlRender* _parent;//父节点string render();//输出html文本void add(HtmlRender* item);//添加html内容void configcnt(string content);//配置html内容void configkws(map<string, string> kws);//配置html内容HtmlRender* parent();list<HtmlRender*> children();//获取子HtmlRender内容
private:list<HtmlRender*> htmlcontent;//html文件内容string tag;//html标签string content;//标签内容map<string, string> kws;//html关键字bool onetag;//是否为单标签bool pre;//是否为原文本内容const map<string, string> ascii_ent = { { "\"", "quot" }, { "\'", "apos" }, { "&", "amp" }, { "<", "lt" }, { ">", "gt" } };//ascii转义void load_content(string content);//加载内容void load_kws(map<string, string> kws);//加载关键字string escape_ascii(string content);//转义ascii字符};string HtmlRender::render() {//渲染为html文本if (this->tag == ""){return this->content;}string htmltext = "<" + this->tag;for (auto &kw : this->kws) {htmltext += " " + kw.first + "=\"" + kw.second + "\"";}htmltext += ">";htmltext += this->content;for (auto item = this->htmlcontent.begin(); item != this->htmlcontent.end(); ++item) {string subtext = (*item)->render();htmltext += "\n" + subtext;}if (this->onetag){//单标签htmltext += "\n";}else{htmltext += "\n</" + this->tag + ">";}return htmltext;
}void HtmlRender::add(HtmlRender* item) {//添加html内容this->htmlcontent.push_back(item);item->_parent = this;
}void HtmlRender::configcnt(string content) {//配置html内容if (this->pre){//原文本内容this->content = content;}else{string ascii_ent = this->escape_ascii(content);this->content = ascii_ent;}}void HtmlRender::configkws(map<string, string> kws) {//配置html内容for (auto &kw : kws) {string value = kw.second;string ascii_ent = this->escape_ascii(value);kw.second = value;}this->kws = kws;}HtmlRender* HtmlRender::parent() {return this->_parent;
}list<HtmlRender*> HtmlRender::children() {return this->htmlcontent;
}void HtmlRender::load_content(string content) {//加载内容string ascii_ent = this->escape_ascii(content);this->content = ascii_ent;}void HtmlRender::load_kws(map<string, string> kws) {//加载关键字for (auto &kw : kws) {string value = kw.second;string ascii_ent = this->escape_ascii(value);kw.second = value;}this->kws = kws;}string HtmlRender::escape_ascii(string content) {//转义ascii字符for (auto &item : this->ascii_ent) {content = subreplace(content, item.first, "&" + item.second + ";");}return content;}

http://www.ppmy.cn/ops/113677.html

相关文章

Android CarrierConfig 配置问题的解决流程

开发步骤 确认代码路径 查看编译用的CarrierConfig APK在项目代码的path&#xff0c;一般是源码或者厂商定制的&#xff1a; packages/apps/CarrierConfig/vendor/mediatek/proprietary/packages/apps/CarrierConfig Note&#xff1a;一些overlay的方式是替换xml文件&#…

gis专业怎么选电脑?

地理信息科学专业&#xff0c;日常会用到CAD制图&#xff0c;以及ArcGIS、MapGIS、谷歌地球等GIS类软件&#xff0c;以及ENVI、Erdas等软件&#xff0c;这些专业软件&#xff0c;需要高配置的CPU/显卡&#xff0c;优良的散热&#xff0c;以及大容量的存储&#xff0c;外加大尺寸…

docker 数据管理

1. 数据管理 1.1 什么是数据管理 docker的镜像是只读的。容器可以进行操作&#xff0c;但数据不能保存到容器中。数据保存需要使用数据卷和数据卷容器。 1.2 容器、数据卷、数据卷容器关系图 1.2.1 什么是数据卷 宿主机的某个目录映射到容器中&#xff0c;作为数据存储的目…

设计模式七大原则

设计模式的七大原则是指导软件设计和架构的基本准则&#xff0c;帮助开发者创建更灵活、可维护和可扩展的系统。以下是这七大原则的详细介绍&#xff1a; 1. 单一职责原则 (SRP: Single Responsibility Principle) 定义 一个类&#xff0c;应当只有一个引起它变化的原因&…

数据结构(Day14)

一、学习内容 结构体 概念 引入&#xff1a;定义整数赋值为10 int a10; 定义小数赋值为3.14 float b3.14; 定义5个整数并赋值 int arr[5] {1 , 2 , 3 , 4 ,5}; 定义一个学生并赋值学号姓名成绩 定义一个雪糕并赋值名称产地单价 问题&#xff1a;没有学生、雪糕 数据类型 解决&…

Docker笔记-Docker Dockerfile

Docker笔记-Docker Dockerfile Dockerfile 是一个用来构建镜像的文本文件&#xff0c;文本内容包含了一条条构建镜像所需的指令和说明。 这里讲解如何运行 Dockerfile 文件来定制一个镜像。 DockerFile构建过程解析&#xff1a; 1、每条保留字指令都必须为大写字母且后面要…

Python类及元类的创建流程

Python类及元类的创建流程 代码运行结果再看type和object的关系和约定type和object具有的方法不一样看代码和运行结果&#xff0c;可以完全理解python的执行过程。再补充几点&#xff0c; 代码 class MetaCls(type):print(0>>>, MetaCls, 0)def __init__(self, name,…

Langchain-chatchat源码部署及测试实验

一年多前接触到Langchain-chatchat的0.2版本,对0.2版本进行了本地部署和大量更新,但0.2版本对最新的大模型支持不够好,部署框架支持也不好且不太稳定,特别是多模态大模型,因此本次主要介绍0.3版本的源码部署,希望对大家有所帮助。Langchain-chatchat从0.3版本开始,支持更…