Markdown转Html

程序设计二 大作业

    设计一款软件可以把markdown语言转化成HTML语言。即输入markdown语言,软件输出HTML语言。软件需要将GFM规范中常用到的Text,Headers,List,Images,Links五项内容转化成HTML语言。从用户的角度出发,还要拥有GUI

一、设计思路

  • 打开文件(判断是否为markdown文件)
  • 读取文件全部内容
  • 输出html标签
  • 选择写入的css样式
  • 主题转换部分使用正则表达式
  • 输出固定的结尾

二、转换方法

1. Text

需要转化的正文部分有加粗,斜体,删除线,简单单行代码块,高亮
以加粗为例,则需要完成的转化为:

info  <b>info</b>**info** \ \Rightarrow \ <b>info</b>

转换时使用正则表达式(s为代转换字符串):

1
2
3
4
5
std::string temp, temp2;
std::regex re_b("\\*\\*([^\\*\\*]+)\\*\\*");
temp = s;
std::regex_replace(std::back_inserter(temp2), temp.begin(), temp.end(), re_b, "<b>$1</b>");
s = temp2;

依次可完成加粗的转换,下面给出不同格式的正则表达式

1
2
3
4
5
6
7
std::regex re_i1("\\*([^\\*]+)\\*");            //斜体
std::regex re_i2("\\_([^\\_]+)\\_"); //斜体
std::regex re_S("\\~\\~([^\\~\\~]+)\\~\\~"); //删除线
std::regex re_b1("\\*\\*([^\\*\\*]+)\\*\\*"); //加粗
std::regex re_b2("\\_\\_([^\\_\\_]+)\\_\\_"); //加粗
std::regex re_code("\\`([^\\`]+)\\`"); //简单代码块
std::regex re_mark("\\=\\=([^\\=\\=]+)\\=\\="); //高亮显示

2. Headers

需要完成的转换为:

##n  text <hn>text</hn>\underbrace{\#\#\cdots}_{n}\ \ text \Rightarrow \ <hn>text</hn>

所以需要先统计’#'出现的次数,再进行转换:

1
2
3
4
5
6
7
8
9
void tr_head(std::string s)
{
int index = 0;
while (s[index] == '#')
index++;
std::smatch answer;
std::regex_match(s, answer, std::regex("^#{1,6} (.+)$"));
std::cout << "<h" << index << ">" << answer[1] << "</h" << index << ">" << std::endl;
}

3. Images

需要完成的转换

![name](url)  <img  src=url">![name](url) \ \Rightarrow \ <img\ \ src=``url">

转换方法同前面类似,下面给出识别的正则表达式

1
std::regex re_image("!\\[.+\\]\\((.+)\\)");

需要完成的转换

[name](url)  <a  href=url">name</a>[name](url) \ \Rightarrow \ <a\ \ href=``url">name</a>

直接给出正则表达式

1
std::regex re_image("\\[.+\\]\\((.+)\\)");

5. Lists

  • 整个翻译开始前先进行列表的递归搜索
  • 先判断是否是列表,然后返回一个奇数或者偶数来代表判断返回的是有序列表还是无序列表,同时该数据还能储存空行数
  • 若是列表,则判断与上一级的前面空格的关系,判断是否是新的一级列表,进入下一次递归,若上一级列表已经结束,则回溯到同一级列表处
  • 整个列表结束时,返回,输出结尾的</ol>或则和</ul>,结束递归

具体的代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
int islist(int i)//判断是否为列表
{
int index = 0;
std::regex a("^[ ]*[-*+] (.+)$");
std::regex e("^[ ]*\\d+[.] (.+)$");
if (std::regex_search(content[i], e))
{
for (int j = 0; content[i][j] == ' '; j++)
index++;
if (index % 4 == 0)
content[i] = std::regex_replace(content[i], e, "<li>$1</li>");
else
return -1;
return 2 * index;
}
else if (std::regex_search(content[i], a))
{
for (int j = 0; content[i][j] == ' '; j++)
index++;
if (index % 4 == 0)
content[i] = std::regex_replace(content[i], a, "<li>$1</li>");
else
return -1;
return 2 * index + 1;
}
else
return -1;
}
int blankjudge(int a)//判断空格无序还是有序
{
if (a % 2 == 0)
return a / 2;
else
return (a - 1) / 2;
}
int handlelist(int i, int blanklast)
{
now = i;
int blankthis = islist(i);
if (blankthis == -1)
return -1;
else
{
int blankthis_temp = blankjudge(blankthis);
int blanklast_temp = blankjudge(blanklast);
if (blankthis % 2 == 0)
{

if (blankthis_temp > blanklast_temp || (blankthis_temp == blanklast_temp && blankthis != blanklast))//新的一层列表
{
content[i].insert(0, "<ol>");
int tmp = handlelist(i + 1, blankthis);
if (tmp == blankthis_temp)
tmp = handlelist(now + 1, blankthis);
content[now - 1] += "</ol>";
return tmp;
}
else if (blankthis_temp == blanklast_temp && blankthis == blanklast)//和上级相同
{
int tmp = handlelist(i + 1, blankthis);
return tmp;
}
else//上一级列表结束
return blankthis_temp;
}
else//同理
{
if (blankthis_temp > blanklast_temp || (blankthis_temp == blanklast_temp && blankthis != blanklast))
{
content[i].insert(0, "<ul>");
int tmp = handlelist(i + 1, blankthis);
if (tmp == blankthis_temp)
tmp = handlelist(now + 1, blankthis);
content[now - 1] += "</ul>";
return tmp;
}
else if (blankthis_temp == blanklast_temp && blankthis == blanklast)
{
int tmp = handlelist(i + 1, blankthis);
return tmp;
}
else
return blankthis_temp;
}
}
return 0;
}

6. Quote

quote的转化和list基本相同,同时免去了有序无序的判断

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
int isquote(int i)
{
std::regex a("^\\>+(.+)$");
if (std::regex_match(content[i], a))
{
int index = 0;
while (content[i][index] == '>')
index++;
content[i] = std::regex_replace(content[i], a, "<p>$1</p>");
return index;
}
else
return 0;
}
void handlequote(int i, int last)
{
now = i;
int number = isquote(i);
if (number == 0)
return;
else if (number >= last)
{
for (int j = 0; j < number - last; j++)
content[i].insert(0, "<blockquote>");
handlequote(now + 1, number);
for (int j = 0; j < number - last; j++)
content[now - 1] += "</blockquote>";
}
}

7. 分割线

较为简单,直接给出转换函数

1
2
3
4
if (std::regex_search(s.at(i), std::regex("^\\-{3,}$")))
printf("<hr/>\n");
if (std::regex_search(s.at(i), std::regex("^\\*{3,}$")))
printf("<hr/>\n");

三、GUI的实现

依托Qt实现程序的图形化,相关内容可查看源码

四、实验感想

  • 学习了markdown及html相关语法
  • 熟悉了正则表达式的使用
  • 熟悉了c++图形化界面的设计

五、源码

        戳这里