Markdown 是一种轻量级的「标记语言」,它的优点很多,目前也被越来越多的写作爱好者,撰稿者广泛使用。看到这里请不要被「标记」、「语言」所迷惑,Markdown 的语法十分简单。常用的标记符号也不超过十个,这种相对于更为复杂的HTML 标记语言来说,Markdown 可谓是十分轻量的,学习成本也不需要太多,且一旦熟悉这种语法规则,会有一劳永逸的效果。
markdown 语法解析器,可以实现将 markdown 语句转换成对应的 html 语句,之后由浏览器负责对 html 渲染。
markdown 语法十分简单,常用的标签有:
代码 (```)
引用 (>)
无序列表 ('*','-','+')
有序列表 ('1.','2.','3.')
标题 (#)
图片 (![]())
链接 ([]())
行内引用 (`)
粗体 (**)
斜体 (*)
表格
具体的使用方式可以参见 Markdown——入门指南 和 Markdown的基本语法
完整代码见 https://github.com/libaoquan95/MarkDownParser
已实现的 markdown 标签:代码、引用、无序列表、有序列表、标题、普通文本、图片、链接、行内引用、粗体、斜体
未实现的 markdown 标签:表格、tab多级结构
主要思路是扫描 markdown 文件,对每一行进行标记,确定每一行的 markdown 标签,之后再根据每一行的 markdown 标签将 markdown 语句转换成 html 语句。
代码区
、引用区
、无序列表区
、有序列表区
,因为这些标签均是可以作用于多行,要根据上下文的 markdown 标签才可以确定其作用范围。在这里需要特别注意 代码区
内不含其它区域,引用区
内可以嵌套其它区域。代码
、引用
、无序列表
、有序列表
、标题
这 5 类可以根据行首元素就能判定出类型的标签,所以不需要扫描全行。图片
,链接
、行内引用
、粗体
、斜体
这 5 类元素并直接转换成 html。扫描文件后,将文件按行存储至内存。相关成员变量如下:
// 按行存储 markdown 文件
private ArrayList<String> mdList = new ArrayList();
// 存储 markdown 文件的每一行对应类型
private ArrayList<String> mdListType = new ArrayList();
将 markdown 文件存入 mdList,之后多次扫描均是直接在 mdList 上进行修改。在本博客中,展示的事例的 markdown 文件如下
## 什么是 markdown
> Markdown 是一种轻量级的「标记语言」,它的优点很多,目前也被越来越多的写作爱好者...
## markdown 常用标签:
代码 (```)
引用 (>)
无序列表 ('*','-','+')
有序列表 ('1.','2.','3.')
标题 (#)
图片 (![]())
链接 ([]())
行内引用 (`)
粗体 (**)
斜体 (*)
表格
```
## markdown 入门1
1. [Markdown——入门指南](http://www.jianshu.com/p/1e402922ee32/)
2. [Markdown的基本语法](http://www.cnblogs.com/libaoquan/p/6812426.html)
### markdown 标签分类
- markdown 标签可以 **简单** 分为 2 大类:...
- 其中,*代码*、`引用`、无序列表、有序列表、标题这 5 类...
- 除了这 5 类标签外,图片,链接、行内引用、粗体、斜体这 5 类...
#### 2.3.3 第一次扫描
在这次扫描中,可以确定出 `代码区` 定位标签 CODE\_BEGIN 和 CODE\_END,`引用区` 定位标签 QUOTE\_BEGIN 和 QUOTE\_END,`无序列表区` 定位标签 UNORDER\_BEGIN 和 UNORDER\_END,`有序列表区` 定位标签 ORDER\_BEGIN 和 ORDER\_END。而其他语句在此次扫描中均暂时定义为 OTHER。
/**
private void defineAreaType() {
// 定位代码区
ArrayList<String> tempList = new ArrayList();
ArrayList<String> tempType = new ArrayList();
tempType.add("OTHER");
tempList.add(" ");
boolean codeBegin = false, codeEnd = false;
for(int i = 1; i < mdList.size() - 1; i++){
String line = mdList.get(i);
if(line.length() > 2 && line.charAt(0) == '`' && line.charAt(1) == '`' && line.charAt(2) == '`') {
// 进入代码区
if(!codeBegin && !codeEnd) {
tempType.add("CODE_BEGIN");
tempList.add(" ");
codeBegin = true;
}
// 离开代码区
else if(codeBegin && !codeEnd) {
tempType.add("CODE_END");
tempList.add(" ");
codeBegin = false;
codeEnd = false;
}
else {
tempType.add("OTHER");
tempList.add(line);
}
}
else {
tempType.add("OTHER");
tempList.add(line);
}
}
tempType.add("OTHER");
tempList.add(" ");
mdList = (ArrayList<String>)tempList.clone();
mdListType = (ArrayList<String>)tempType.clone();
tempList.clear();
tempType.clear();
// 定位其他区,注意代码区内无其他格式
boolean isCodeArea = false;
tempList.add(" ");
tempType.add("OTHER");
for(int i = 1; i < mdList.size() - 1; i++){
String line = mdList.get(i);
String lastLine = mdList.get(i - 1);
String nextLine = mdList.get(i + 1);
if(mdListType.get(i) == "CODE_BEGIN") {
isCodeArea = true;
tempList.add(line);
tempType.add("CODE_BEGIN");
continue;
}
if(mdListType.get(i) == "CODE_END") {
isCodeArea = false;
tempList.add(line);
tempType.add("CODE_END");
continue;
}
// 代码区不含其他格式
if(!isCodeArea) {
// 进入引用区
if(line.length() > 2 && line.charAt(0) == '>' && lastLine.charAt(0) != '>' && nextLine.charAt(0) == '>') {
tempList.add(" ");
tempList.add(line);
tempType.add("QUOTE_BEGIN");
tempType.add("OTHER");
}
// 离开引用区
else if(line.length() > 2 && line.charAt(0) == '>' && lastLine.charAt(0) == '>' && nextLine.charAt(0) != '>') {
tempList.add(line);
tempList.add(" ");
tempType.add("OTHER");
tempType.add("QUOTE_END");
}
// 单行引用区
else if(line.length() > 2 && line.charAt(0) == '>' && lastLine.charAt(0) != '>' && nextLine.charAt(0) != '>') {
tempList.add(" ");
tempList.add(line);
tempList.add(" ");
tempType.add("QUOTE_BEGIN");
tempType.add("OTHER");
tempType.add("QUOTE_END");
}
// 进入无序列表区
else if((line.charAt(0) == '-' && lastLine.charAt(0) != '-' && nextLine.charAt(0) == '-') ||
(line.charAt(0) == '+' && lastLine.charAt(0) != '+' && nextLine.charAt(0) == '+') ||
(line.charAt(0) == '*' && lastLine.charAt(0) != '*' && nextLine.charAt(0) == '*')){
tempList.add(" ");
tempList.add(line);
tempType.add("UNORDER_BEGIN");
tempType.add("OTHER");
}
// 离开无序列表区
else if((line.charAt(0) == '-' && lastLine.charAt(0) == '-' && nextLine.charAt(0) != '-') ||
(line.charAt(0) == '+' && lastLine.charAt(0) == '+' && nextLine.charAt(0) != '+') ||
(line.charAt(0) == '*' && lastLine.charAt(0) == '*' && nextLine.charAt(0) != '*')){
tempList.add(line);
tempList.add(" ");
tempType.add("OTHER");
tempType.add("UNORDER_END");
}
// 单行无序列表区
else if((line.charAt(0) == '-' && lastLine.charAt(0) != '-' && nextLine.charAt(0) != '-') ||
(line.charAt(0) == '+' && lastLine.charAt(0) != '+' && nextLine.charAt(0) != '+') ||
(line.charAt(0) == '*' && lastLine.charAt(0) != '*' && nextLine.charAt(0) != '*')){
tempList.add(" ");
tempList.add(line);
tempList.add(" ");
tempType.add("UNORDER_BEGIN");
tempType.add("OTHER");
tempType.add("UNORDER_END");
}
// 进入有序列表区
else if((line.length() > 1 && (line.charAt(0) >= '1' || line.charAt(0) <= '9') && (line.charAt(1) == '.')) &&
!(lastLine.length() > 1 && (lastLine.charAt(0) >= '1' || line.charAt(0) <= '9') && (lastLine.charAt(1) == '.')) &&
(nextLine.length() > 1 && (nextLine.charAt(0) >= '1' || line.charAt(0) <= '9') && (nextLine.charAt(1) == '.'))){
tempList.add(" ");
tempList.add(line);
tempType.add("ORDER_BEGIN");
tempType.add("OTHER");
}
// 离开有序列表区
else if((line.length() > 1 && (line.charAt(0) >= '1' || line.charAt(0) <= '9') && (line.charAt(1) == '.')) &&
(lastLine.length() > 1 && (lastLine.charAt(0) >= '1' || line.charAt(0) <= '9') && (lastLine.charAt(1) == '.')) &&
!(nextLine.length() > 1 && (nextLine.charAt(0) >= '1' || line.charAt(0) <= '9') && (nextLine.charAt(1) == '.'))){
tempList.add(line);
tempList.add(" ");
tempType.add("OTHER");
tempType.add("ORDER_END");
}
// 单行有序列表区
else if((line.length() > 1 && (line.charAt(0) >= '1' || line.charAt(0) <= '9') && (line.charAt(1) == '.')) &&
!(lastLine.length() > 1 && (lastLine.charAt(0) >= '1' || line.charAt(0) <= '9') && (lastLine.charAt(1) == '.')) &&
!(nextLine.length() > 1 && (nextLine.charAt(0) >= '1' || line.charAt(0) <= '9') && (nextLine.charAt(1) == '.'))){
tempList.add(" ");
tempList.add(line);
tempList.add(" ");
tempType.add("ORDER_BEGIN");
tempType.add("OTHER");
tempType.add("ORDER_END");
}
// 其他
else {
tempList.add(line);
tempType.add("OTHER");
}
}
else {
tempList.add(line);
tempType.add("OTHER");
}
}
tempList.add(" ");
tempType.add("OTHER");
mdList = (ArrayList<String>)tempList.clone();
mdListType = (ArrayList<String>)tempType.clone();
tempList.clear();
tempType.clear();
}
第一次扫描后,markdown 格式如下,左侧为每一行的标签类型,右侧为文件内容:
OTHER
OTHER ## 什么是 markdown
QUOTE_BEGIN
OTHER > Markdown 是一种轻量级的「标记语言」,它的优点很多,目前也被越来越多的写作爱好者...
QUOTE_END
OTHER
OTHER ## markdown 常用标签:
CODE_BEGIN
OTHER 代码 (`
)
OTHER 引用 (>)
OTHER 无序列表 ('*','-','+')
OTHER 有序列表 ('1.','2.','3.')
OTHER 标题 (#)
OTHER 图片 (![]())
OTHER 链接 ([]())
OTHER 行内引用 (`)
OTHER 粗体 (**)
OTHER 斜体 (*)
OTHER 表格
CODE_END
OTHER ## markdown 入门1
ORDER_BEGIN
OTHER 1. Markdown——入门指南
OTHER 2. Markdown的基本语法
ORDER_END
OTHER
OTHER ### markdown 标签分类
UNORDER_BEGIN
OTHER - markdown 标签可以 简单 分为 2 大类:...
OTHER - 其中,代码、引用
、无序列表、有序列表、标题这 5 类...
OTHER - 除了这 5 类标签外,图片,链接、行内引用、粗体、斜体这 5 类...
UNORDER_END
OTHER
#### 2.3.4 第二次扫描
在这次扫描中,可以确定出 `代码行` 标签 CODE_LINE, `无序列表行` 标签 UNORDER_LINE, `有序列表行` 标签 ORDER_LINE, `空行` 标签 BLANK_LINE, `标题行` 标签 TITLE。
/**
*/
private void defineLineType() {
Stack<String> st = new Stack();
for(int i = 0; i < mdList.size(); i++){
String line = mdList.get(i);
String typeLine = mdListType.get(i);
if(typeLine == "QUOTE_BEGIN" || typeLine == "UNORDER_BEGIN" || typeLine == "ORDER_BEGIN" || typeLine == "CODE_BEGIN") {
st.push(typeLine);
}
else if(typeLine == "QUOTE_END" || typeLine == "UNORDER_END" || typeLine == "ORDER_END" || typeLine == "CODE_END") {
st.pop();
}
else if(typeLine == "OTHER") {
if(!st.isEmpty()) {
// 引用行
if(st.peek() == "QUOTE_BEGIN") {
mdList.set(i, line.trim().substring(1).trim());
}
// 无序列表行
else if(st.peek() == "UNORDER_BEGIN") {
mdList.set(i, line.trim().substring(1).trim());
mdListType.set(i, "UNORDER_LINE");
}
// 有序列表行
else if(st.peek() == "ORDER_BEGIN") {
mdList.set(i, line.trim().substring(2).trim());
mdListType.set(i, "ORDER_LINE");
}
// 代码行
else {
mdListType.set(i, "CODE_LINE");
}
}
line = mdList.get(i);
typeLine = mdListType.get(i);
// 空行
if(line.trim().isEmpty()) {
mdListType.set(i, "BLANK_LINE");
mdList.set(i, "");
}
// 标题行
else if(line.trim().charAt(0) == '#') {
mdListType.set(i, "TITLE");
mdList.set(i, line.trim());
}
}
}
}
第二次扫描后,markdown 格式如下,左侧为每一行的标签类型,右侧为文件内容:
BLANK_LINE
TITLE ## 什么是 markdown
QUOTE_BEGIN
OTHER Markdown 是一种轻量级的「标记语言」,它的优点很多,目前也被越来越多的写作爱好者...
QUOTE_END
BLANK_LINE
TITLE ## markdown 常用标签:
CODE_BEGIN
CODE_LINE 代码 (`
)
CODE_LINE 引用 (>)
CODE_LINE 无序列表 ('*','-','+')
CODE_LINE 有序列表 ('1.','2.','3.')
CODE_LINE 标题 (#)
CODE_LINE 图片 (![]())
CODE_LINE 链接 ([]())
CODE_LINE 行内引用 (`)
CODE_LINE 粗体 (**)
CODE_LINE 斜体 (*)
CODE_LINE 表格
CODE_END
TITLE ## markdown 入门1
ORDER_BEGIN
ORDER_LINE Markdown——入门指南
ORDER_LINE Markdown的基本语法
ORDER_END
BLANK_LINE
TITLE ### markdown 标签分类
UNORDER_BEGIN
UNORDER_LINE markdown 标签可以 简单 分为 2 大类:...
UNORDER_LINE 其中,代码、引用
、无序列表、有序列表、标题这 5 类...
UNORDER_LINE 除了这 5 类标签外,图片,链接、行内引用、粗体、斜体这 5 类...
UNORDER_END
BLANK_LINE
#### 2.3.5 第三次扫描
在这次扫描中,根据每一行的标签,将其转化为 html 代码,并行内扫描确定 `图片`、`链接`、 `行内引用`、 `粗体`、 `斜体`。
/**
*/
private void translateToHtml() {
for(int i = 0; i < mdList.size(); i++){
String line = mdList.get(i);
String typeLine = mdListType.get(i);
// 是空行
if(typeLine == "BLANK_LINE") {
mdList.set(i, "");
}
// 是普通文本行
else if(typeLine == "OTHER") {
mdList.set(i, "<p>" + translateToHtmlInline(line.trim()) + "</p>");
}
// 是标题行
else if(typeLine == "TITLE") {
int titleClass = 1;
for(int j = 1; j < line.length(); j++) {
if(line.charAt(j) == '#') {
titleClass++;
}
else {
break;
}
}
mdList.set(i, "<h" + titleClass + ">"+ translateToHtmlInline(line.substring(titleClass).trim()) +"</h" + titleClass + ">");
}
// 是无序列表行
else if(typeLine == "UNORDER_BEGIN") {
mdList.set(i, "<ul>");
}
else if(typeLine == "UNORDER_END") {
mdList.set(i, "</ul>");
}
else if(typeLine == "UNORDER_LINE") {
mdList.set(i, "<li>" + translateToHtmlInline(line.trim()) + "</li>");
}
// 是有序列表行
else if(typeLine == "ORDER_BEGIN") {
mdList.set(i, "<ol>");
}
else if(typeLine == "ORDER_END") {
mdList.set(i, "</ol>");
}
else if(typeLine == "ORDER_LINE") {
mdList.set(i, "<li>" + translateToHtmlInline(line.trim()) + "</li>");
}
// 是代码行
else if(typeLine == "CODE_BEGIN") {
mdList.set(i, "<pre>");
}
else if(typeLine == "CODE_END") {
mdList.set(i, "</pre>");
}
else if(typeLine == "CODE_LINE") {
mdList.set(i, "<code>" + line + "</code>");
}
// 是引用行
else if(typeLine == "QUOTE_BEGIN") {
mdList.set(i, "<blockquote>");
}
else if(typeLine == "QUOTE_END"){
mdList.set(i, "</blockquote>");
}
}
}
/**
*/
private String translateToHtmlInline( String line) {
String html = "";
for(int i=0; i<line.length();i++) {
// 图片
if(i < line.length() - 4 && line.charAt(i) == '!' && line.charAt(i + 1) == '[') {
int index1 = line.indexOf(']', i + 1);
if(index1 != -1 && line.charAt(index1 + 1) == '(' && line.indexOf(')', index1 + 2) != -1){
int index2 = line.indexOf(')', index1 + 2);
String picName = line.substring(i + 2, index1);
String picPath = line.substring(index1 + 2, index2);
line = line.replace(line.substring(i, index2 + 1), "<img alt='" + picName + "' src='" + picPath + "' />");
}
}
// 链接
if(i < line.length() - 3 && ((i > 0 && line.charAt(i) == '[' && line.charAt(i - 1) != '!') || (line.charAt(0) == '['))) {
int index1 = line.indexOf(']', i + 1);
if(index1 != -1 && line.charAt(index1 + 1) == '(' && line.indexOf(')', index1 + 2) != -1){
int index2 = line.indexOf(')', index1 + 2);
String linkName = line.substring(i + 1, index1);
String linkPath = line.substring(index1 + 2, index2);
line = line.replace(line.substring(i, index2 + 1), "<a href='" + linkPath + "'> " + linkName + "</a>");
}
}
// 行内引用
if(i < line.length() - 1 && line.charAt(i) == '`' && line.charAt(i + 1) != '`') {
int index = line.indexOf('`', i + 1);
if(index != -1) {
String quoteName = line.substring(i + 1, index);
line = line.replace(line.substring(i, index + 1), "<code>" + quoteName + "</code>");
}
}
// 粗体
if(i < line.length() - 2 && line.charAt(i) == '*' && line.charAt(i + 1) == '*') {
int index = line.indexOf("**", i + 1);
if(index != -1) {
String quoteName = line.substring(i + 2, index );
line = line.replace(line.substring(i, index + 2), "<strong>" + quoteName + "</strong>");
}
}
// 斜体
if(i < line.length() - 2 && line.charAt(i) == '*' && line.charAt(i + 1) != '*') {
int index = line.indexOf('*', i + 1);
if(index != -1 && line.charAt(index + 1) != '*') {
String quoteName = line.substring(i + 1, index);
line = line.replace(line.substring(i, index + 1), "<i>" + quoteName + "</i>");
}
}
}
return line;
}
第三次扫描后,markdown 格式如下,左侧为每一行的标签类型,右侧为文件内容:
BLANK_LINE
TITLE <h2>什么是 markdown</h2>
QUOTE_BEGIN <blockquote>
OTHER <p>Markdown 是一种轻量级的「标记语言」,它的优点很多,目前也被越来越多的写作爱好者...</p>
QUOTE_END </blockquote>
BLANK_LINE
TITLE <h2>markdown 常用标签:</h2>
CODE_BEGIN <pre>
CODE_LINE 代码 (
`
)
CODE_LINE 引用 (>)
CODE_LINE 无序列表 ('*','-','+')
CODE_LINE 有序列表 ('1.','2.','3.')
CODE_LINE 标题 (#)
CODE_LINE 图片 (![]())
CODE_LINE 链接 ([]())
CODE_LINE 行内高亮 (`)
CODE_LINE 粗体 (**)
CODE_LINE 斜体 (*)
CODE_LINE 表格
CODE_END </pre>
TITLE <h2>markdown 入门1</h2>
ORDER_BEGIN <ol>
ORDER_LINE <li> Markdown——入门指南</li>
ORDER_LINE <li> Markdown的基本语法</li>
ORDER_END </ol>
BLANK_LINE
TITLE <h3>markdown 标签分类</h3>
UNORDER_BEGIN <ul>
UNORDER_LINE <li>markdown 标签可以 简单 分为 2 大类:...</li>
UNORDER_LINE <li>其中,代码、引用
、无序列表、有序列表、标题这 5 类...</li>
UNORDER_LINE <li>除了这 5 类标签外,图片,链接、行内高亮、粗体、斜体这 5 类...</li>
UNORDER_END </ul>
BLANK_LINE
至此,将 mdList 与 html 头部 与 尾部 写入 html 文件即可。
---------------------------------------------------
原网址: [访问](https://www.cnblogs.com/libaoquan/p/7442843.html)
创建于: 2021-06-17 12:45:57
目录: default
未标明原创文章均为采集,版权归作者所有,转载无需和我联系,请注明原出处,南摩阿彌陀佛,知识,不只知道,要得到
最新评论