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

HtmlRender

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

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

简介

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


使用接口

初始化

cpp 复制代码
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文本

cpp 复制代码
string result = HtmlRender.render()

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

添加子元素

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

cpp 复制代码
HtmlRender* subitem = new HtmlRender(...)
item.add(subitem)

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

cpp 复制代码
item->add(subitem)

设置标签元素内容和参数

设置内容:

cpp 复制代码
item.configcnt(string content)

设置参数:

cpp 复制代码
item.configkws(map<string, string> kws)

获取父元素指针

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

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

cpp 复制代码
HtmlRender* p_item = subitem->parent()
//p_item == &item

获取子元素指针列表

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

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

cpp 复制代码
list<HtmlRender*> children = item.children()

实现原理

添加子元素

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

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

渲染

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

cpp 复制代码
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;
}

示例

测试代码:

cpp 复制代码
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>

主要源码

cpp 复制代码
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使用了pos
    while((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;
 }
相关推荐
LJ小番茄12 分钟前
Vue 常见的几种通信方式(总结)
前端·javascript·vue.js·html
Lenyiin29 分钟前
《 C++ 修炼全景指南:十 》自平衡的艺术:深入了解 AVL 树的核心原理与实现
数据结构·c++·stl
程序猿练习生1 小时前
C++速通LeetCode中等第5题-无重复字符的最长字串
开发语言·c++·leetcode
无名之逆1 小时前
云原生(Cloud Native)
开发语言·c++·算法·云原生·面试·职场和发展·大学期末
好蛊1 小时前
第 2 课 春晓——cout 语句
c++·算法
景小雨4 小时前
【数据结构与算法】排序算法之快速排序(简)
c++·算法·排序算法·快速排序
鸽嗷高.4 小时前
C++伟大发明--模版
开发语言·c++
软件技术NINI5 小时前
HTML——基本标签
前端·javascript·html
weixin_486681146 小时前
C++系列-STL容器中统计算法count, count_if
开发语言·c++·算法
基德爆肝c语言6 小时前
C++入门
开发语言·c++