Markdown 格式转换为 Html 格式 (C++)源码

源码压缩包

源码压缩包 https://download.csdn.net/download/qq_46457493/20368583?spm=1001.2014.3001.5503

Main.cpp 函数

#include "markdownParser.h"

int main() {
	string m;
	cout << "输入读取的文件名: ";
	cin >> m;
	markdownPaerser mk(m);
	mk.transferm();
	mk.html();
	return 0;
}

markdownPaerser.h 函数

#pragma once
#pragma once
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
using namespace std;

enum Token {
	nul = 0,
	paragraph = 1,//段落
	href = 2,//超链接
	ul = 3,//无序集
	ol = 4,//有序集
	li = 5,//列表项目
	em = 6,//斜体,表示强调
	strong = 7,//加粗
	hr = 8,//标题
	image = 9,//图片
	quote = 10,//块引用

	//标题1-6
	h1 = 11,
	h2 = 12,
	h3 = 13,
	h4 = 14,
	h5 = 15,
	h6 = 16,

	blockcode = 17,//表示计算机源代码
	code = 18,//单行代码
};

// HTML 前置标签
const std::string frontTag[] = {
 "", "<p>", "", "<ul>", "<ol>", "<li>", "<em>", "<strong>", "<hr color=#CCCCCC size=1 / > ",
 "", "<blockquote>", "<h1>", "<h2>", "<h3>", "<h4>", "<h5>", "<h6>",
 "<pre><code>", "<code>" };
// HTML 后置标签
const std::string backTag[] = {
	"", "</p>", "", "</ul>", "</ol>", "</li>", "</em>",
	"</strong>", "", "", "</blockquote>", "</h1>", "</h2>",
	"</h3>", "</h4>", "</h5>", "</h6>", "</code></pre>", "</code>" };

struct Node {
	//语法类型
	int _type;
	//孩子节点
	vector<Node*> _child;
	//内容
	//elem[0]:保存需要显示的内容
	//elem[1]:保存网址/路径
	string elem[2];

	Node(int type)
	{
		_type = type;//赋值
	}
};

class markdownPaerser {
public:
	markdownPaerser(const string &filename)
	{
		_root = new Node(nul);
		_filename = filename;
	}
	~markdownPaerser() {
		if (_root) {
			destory(_root);
		}
	}
	void transferm();
	void dfs(Node* root);//语法树转换成HTML源代码//深度优先遍历 DFS
	const char* processStr(const char* str);//去除行首空格
	void insert(Node* curNode, const char* str);//逐字符内容插入
	pair<int, const char*> parseType(const char* str);//解析行首语法  返回子:语法类型 + 对应内容起始位置
	bool isCutLine(const char* str);//判断水平分割线 "---"
	void html();
	void destory(Node* root);//销毁
private:
	Node* _root;//语法树根节点
	string _filename;//文件名
	string _content;//存放HTML文档内容
};



markdownPaerser.cpp 函数

#include "markdownParser.h"

void markdownPaerser::transferm()
{
	//打开文件
	ifstream fin(_filename);
	if (!fin.is_open()) {	//打开失败
		cerr << "打开文件失败" << endl;
		return;
	}

	bool inblock = false;
	//读取内容(按行读取)
	string rowStr;
	while (fin.peek() != EOF) {	//fin.eof()
		getline(fin, rowStr);

		//预处理:处理行首空格
		const char* start = processStr(rowStr.c_str());
		//忽略空内容
		if (!inblock && start == nullptr)	continue;

		//判断是否为水平分割线
		if (!inblock && isCutLine(start)) {
			_root->_child.push_back(new Node(hr));
			continue;
		}

		//语法解析//字典
		pair<int, const char*> typeRet = parseType(start);

		//创建语法结点

		//代码块结点
		if (typeRet.first == blockcode) {
			//判断代码块起始还是结束
			if (!inblock) {
				//代码块起始
				//创建代码块结点
				_root->_child.push_back(new Node(blockcode));
			}

			//如果是代码块结束位置,不需要创建新的代码块结点

			inblock = !inblock;
			continue;
		}

		//判断是否为代码块中代码
		if (inblock) {
			_root->_child.back()->elem[0] += rowStr;
			_root->_child.back()->elem[0] += '\n';
			continue;
		}

		//段落结点
		if (typeRet.first == paragraph) {
			//创建一个段落结点
			_root->_child.push_back(new Node(paragraph));
			//逐字符插入
			insert(_root->_child.back(), typeRet.second);
			continue;
		}

		//标题结点
		if (typeRet.first >= h1 && typeRet.first <= h6) {
			//创建标题结点
			_root->_child.push_back(new Node(typeRet.first));
			//插入标题内容
			_root->_child.back()->elem[0] = typeRet.second;
			continue;
		}

		//无序列表
		if (typeRet.first == ul) {
			//判断是否为无序列表的第一项
			//文档开始,或者语法书最后一个结点不是无序列表结点
			if (_root->_child.empty() || _root->_child.back()->_type != ul) {
				//创建无序列表
				_root->_child.push_back(new Node(ul));
			}
			//给无序列表加入列表子节点
			Node* uNode = _root->_child.back();
			uNode->_child.push_back(new Node(li));
			//给列表子节点插入内容
			insert(uNode->_child.back(), typeRet.second);
			continue;
		}

		//有序列表
		if (typeRet.first == ol) {
			if (_root->_child.empty() || _root->_child.back()->_type != ol) {
				_root->_child.push_back(new Node(ol));
			}
			Node* oNode = _root->_child.back();
			oNode->_child.push_back(new Node(li));
			insert(oNode->_child.back(), typeRet.second);
			continue;
		}

		//引用
		if (typeRet.first == quote) {
			//创建引用结点
			_root->_child.push_back(new Node(quote));
			insert(_root->_child.back(), typeRet.second);
		}
	}
	//展开语法树,生成HTML文档
	dfs(_root);
}

void markdownPaerser::dfs(Node* root)
{
	//插入前置标签
	_content += frontTag[root->_type];

	//插入内容

	//网址
	if (root->_type == href) {
		_content += "<a href=\"";
		_content += root->elem[1];
		_content += "\">";
		_content += root->elem[0];
		_content += "</a>";
	}
	//图片
	else if (root->_type == image) {
		_content += "<img alt=\"";
		_content += root->elem[0];
		_content += "\" src=\"";
		_content += root->elem[1];
		_content += "\" />";
	}
	//其他
	else {
		_content += root->elem[0];
	}

	//处理孩子结点
	for (Node* ch : root->_child) {
		dfs(ch);
	}

	//插入后置标签
	_content += backTag[root->_type];
}

const char* markdownPaerser::processStr(const char* str)
{
	while (*str) {
		if (*str == ' ' || *str == '\t')	++str;
		else break;
	}
	if (*str == '\0')	return nullptr;
	return str;
}

void markdownPaerser::insert(Node* curNode, const char* str)
{
	bool incode = false;	//是否为行内代码
	bool instrong = false;	//粗体
	bool inem = false;		//斜体
	int len = strlen(str);
	//解析内容为纯文本,可以放入纯文本结点中
	//先创建一个纯文本孩子结点
	curNode->_child.push_back(new Node(nul));
	for (int i = 0; i < len; ++i) {
		//1.行内代码
		if (str[i] == '`') {
			if (incode) {
				//行内代码结束,则创建一个新的孩子结点,存放后序内容
				curNode->_child.push_back(new Node(nul));
			}
			else {
				//创建行内代码
				curNode->_child.push_back(new Node(code));
			}
			incode = !incode;
			continue;
		}

		//2.粗体: "**  **"
		//出现"**"且不在行内代码中
		if (str[i] == '*' && i + 1 < len && str[i + 1] == '*' && !incode) {
			if (instrong) {
				//粗体结束,创建新结点
				curNode->_child.push_back(new Node(nul));
			}
			else {
				//创建新的粗体结点
				curNode->_child.push_back(new Node(strong));
			}

			instrong = !instrong;
			//跳过粗体语法
			++i;
			continue;
		}

		//3.斜体:	"_   _"
		if (str[i] == '_' && !incode) {
			if (inem) {
				curNode->_child.push_back(new Node(nul));
			}
			else {
				curNode->_child.push_back(new Node(em));
			}
			inem = !inem;
			continue;
		}

		//4.图片:	![图片名称](图片地址)
		if (str[i] == '!' && i + 1 < len && str[i + 1] == '[') {
			//创建图片结点
			curNode->_child.push_back(new Node(image));
			Node* iNode = curNode->_child.back();
			i += 2;
			//存放图片名称在elem[0]
			for (; i < len && str[i] != ']'; ++i) {
				iNode->elem[0] += str[i];
			}
			//存放图片地址在elem[1]
			i += 2;
			for (; i < len && str[i] != ')'; ++i) {
				iNode->elem[1] += str[i];
			}

			//创建新结点处理后续内容
			curNode->_child.push_back(new Node(nul));
			continue;
		}

		//5.链接:[链接名](网址)
		//有左括号,且不在行内代码
		if (str[i] == '[' && !incode) {
			//创建链接结点
			curNode->_child.push_back(new Node(href));
			Node* hNode = curNode->_child.back();
			++i;
			//存放链接名在elem[0]
			for (; i < len && str[i] != ']'; ++i) {
				hNode->elem[0] += str[i];
			}
			//存放链接地址在elem[1]
			i += 2;
			for (; i < len && str[i] != ')'; ++i) {
				hNode->elem[1] += str[i];
			}

			//创建新结点处理后续内容
			curNode->_child.push_back(new Node(nul));
			continue;

		}

		//6.普通纯文本
		curNode->_child.back()->elem[0] += str[i];
	}

}

pair<int, const char*> markdownPaerser::parseType(const char* str)
{
	//解析标题:# + 空格
	const char* ptr = str;
	int titleNum = 0;
	while (*ptr && *ptr == '#') {
		++ptr;
		++titleNum;
	}
	//1.标题
	if (*ptr == ' ' && titleNum > 0 && titleNum <= 6) {
		return make_pair(h1 + titleNum - 1, ptr + 1);
	}
	//不符合标题语法,需要重新解析
	ptr = str;

	//2.代码块:```代码内容```
	if (strncmp(ptr, "```", 3) == 0) {
		return make_pair(blockcode, ptr + 3);
	}

	//3.无序列表: - + 空格
	if (strncmp(ptr, "- ", 2) == 0) {
		return make_pair(ul, ptr + 2);
	}

	//4.有序列表:数字字符 + "." + 空格
	if (*ptr >= '0' && *ptr <= '9') {
		//遍历完数字
		while (*ptr && (*ptr >= '0' && *ptr <= '9'))	++ptr;
		if (*ptr && *ptr == '.') {
			++ptr;
			if (*ptr && *ptr == ' ')	return make_pair(ol, ptr + 1);
		}
	}
	//重置
	ptr = str;
	//5.引用:">" + 空格
	if (strncmp(ptr, "> ", 2) == 0) {
		return make_pair(quote, ptr + 2);
	}

	//其他语法统一解析为段落
	return make_pair(paragraph, str);
}

bool markdownPaerser::isCutLine(const char* str)
{
	int cnt = 0;
	while (*str && *str == '-') {
		++str;
		++cnt;
	}
	return cnt >= 3;
}

void markdownPaerser::html()
{
	std::string head =
		"<!DOCTYPE html><html><head>\
        <meta charset=\"utf-8\">\
        <title>Markdown</title>\
        <link rel=\"stylesheet\" href=\"github-markdown.css\">\
        </head><body><article class=\"markdown-body\">";
	std::string end = "</article></body></html>";

	string m;
	cout << "输入写入的文件名: ";
	cin >> m;

	ofstream fout(m);
	fout << head << _content << end;
}

void markdownPaerser::destory(Node* root)
{
	if (root) {
		for (Node* ch : root->_child) {
			destory(ch);
		}

		delete root;
	}
}

测试用例 1

## 接口名称

### 1) 请求地址

>http://apis.baidu.com/apistore/idservice/id?a=xx&b=xx

### 2) 调用方式:HTTP post

### 3) 接口描述:

* 接口描述详情

### 4) 请求参数:

#### GET参数:

| 字段名称 | 字段说明 |  类型  | 必填 | 备注 |
| -------- | :------: | :----: | :--: | ---: |
| a        |          | string |  Y   |    - |
| b        |          | string |  Y   |    - |


#### POST参数:

| 字段名称 | 字段说明 |  类型  | 必填 | 备注 |
| -------- | :------: | :----: | :--: | ---: |
| d        |          | string |  Y   |    - |
| e        |          | string |  Y   |    - |

### 5) 请求返回结果:


'''
{
    "errNum": 300202,
    "errMsg": "Missing apikey"
}
'''



### 6) 请求返回结果参数说明:

| 字段名称 | 字段说明 |  类型  | 必填 | 备注 |
| -------- | :------: | :----: | :--: | ---: |
| errNum   |          | string |  Y   |    - |
| errMsg   |          | string |  Y   |    - |

结果一

接口名称
1) 请求地址
>http://apis.baidu.com/apistore/idservice/id?a=xx&b=xx

2) 调用方式:HTTP post
3) 接口描述:
* 接口描述详情

4) 请求参数:
GET参数:
|字段名称 |字段说明 |类型 |必填 |备注 |

| -------------|:--------------:|:--------------:|:--------------:| ------:|

|a||string|Y|-|

|b||string|Y|-|

POST参数:
|字段名称 |字段说明 |类型 |必填 |备注 |

| -------------|:--------------:|:--------------:|:--------------:| ------:|

|d||string|Y|-|

|e||string|Y|-|

5) 请求返回结果:
{
    "errNum": 300202,
    "errMsg": "Missing apikey"
}
6) 请求返回结果参数说明:
|字段名称 |字段说明 |类型 |必填 |备注 |

| -------------|:--------------:|:--------------:|:--------------:| ------:|

|errNum||string|Y|-|

|errMsg||string|Y|-|

测试用例 2

# 轻量级标记语言 Markdown

## 起源

Markdown是[Daring Fireball](http://daringfireball.net/projects/markdown/syntax)的作者John Gruber发明,身为Blogger,可能他也觉得传统的HTML有些复杂吧,退一步来讲,若是一个毫无计算机基础的Blogger,根本也没必要去掌握那些复杂的标签语言知识。

## 发展

越来越多的软件和服务支持 Markdown 语法,应该说,Mac上大多数的写作软件都支持它。在线工具同样有很多,如果你的博客基于Wordpress或是 blogger,它同样支持发布。

不仅仅是写博客,一切文章都可以用Markdown语法来写,比如说你想将一个标题加大字体,只需要在相应文字前面加上一个#或是在它的下一行加上一些等号即可,还有比这更简单的调整格式方式吗?

## 语法参见

[官方语法说明](http://daringfireball.net/projects/markdown/syntax)

## 示例

---

## 标题

# 一级标题

## 二级标题

### 三级标题

#### 四级标题

##### 五级标题

###### 六级标题

## 强调

- **粗体**

- _斜体_

## 引用

> 区块引用

## 列表

- red
- green
- blue

1. red
2. green
3. blue

## 代码

代码的插入方式 `printf('\n');`

'''
function method()
{
    alert("javascript");
}
'''

'''
class Test{
    public static void main(String argc[]){
        System.out.println("java");
    }
}
'''

'''
class Test{
    public static void main()
    {
        Console.WriteLine("C#");
    }
}
'''

## 链接

行内连接 [GitHub](https://github.com/) 的链接

## 图片

![shiyanlou logo](https://img-blog.csdnimg.cn/img_convert/9ab162ddffa3a31dcddd562064a8b6ba.png)

结果二

忘记运行了。。。,自己试吧
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

木18

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值