Markdown 是一种轻量级的「标记语言」,它的优势不少,目前也被愈来愈多的写做爱好者,撰稿者普遍使用。看到这里请不要被「标记」、「语言」所迷惑,Markdown 的语法十分简单。经常使用的标记符号也不超过十个,这种相对于更为复杂的HTML 标记语言来讲,Markdown 可谓是十分轻量的,学习成本也不须要太多,且一旦熟悉这种语法规则,会有一劳永逸的效果。html
markdown 语法解析器,能够实现将 markdown 语句转换成对应的 html 语句,以后由浏览器负责对 html 渲染。java
markdown 语法十分简单,经常使用的标签有:git
代码 (```) 引用 (>) 无序列表 ('*','-','+') 有序列表 ('1.','2.','3.') 标题 (#) 图片 (![]()) 连接 ([]()) 行内引用 (`) 粗体 (**) 斜体 (*) 表格
具体的使用方式能够参见 Markdown——入门指南 和 Markdown的基本语法github
完整代码见 https://github.com/libaoquan95/MarkDownParser
已实现的 markdown 标签:代码、引用、无序列表、有序列表、标题、普通文本、图片、连接、行内引用、粗体、斜体
未实现的 markdown 标签:表格、tab多级结构浏览器
主要思路是扫描 markdown 文件,对每一行进行标记,肯定每一行的 markdown 标签,以后再根据每一行的 markdown 标签将 markdown 语句转换成 html 语句。markdown
代码区
、引用区
、无序列表区
、有序列表区
,由于这些标签均是能够做用于多行,要根据上下文的 markdown 标签才能够肯定其做用范围。在这里须要特别注意 代码区
内不含其它区域,引用区
内能够嵌套其它区域。代码
、引用
、无序列表
、有序列表
、标题
这 5 类能够根据行首元素就能断定出类型的标签,因此不须要扫描全行。图片
,连接
、行内引用
、粗体
、斜体
这 5 类元素并直接转换成 html。扫描文件后,将文件按行存储至内存。相关成员变量以下:学习
// 按行存储 markdown 文件 private ArrayList<String> mdList = new ArrayList(); // 存储 markdown 文件的每一行对应类型 private ArrayList<String> mdListType = new ArrayList();
将 markdown 文件存入 mdList,以后屡次扫描均是直接在 mdList 上进行修改。在本博客中,展现的事例的 markdown 文件以下code
## 什么是 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 类...
在此次扫描中,能够肯定出 代码区
定位标签 CODE_BEGIN 和 CODE_END,引用区
定位标签 QUOTE_BEGIN 和 QUOTE_END,无序列表区
定位标签 UNORDER_BEGIN 和 UNORDER_END,有序列表区
定位标签 ORDER_BEGIN 和 ORDER_END。而其余语句在这次扫描中均暂时定义为 OTHER。htm
/** * 判断每一段 markdown 语法对应的 html 类型 * @param 空 * @return 空 */ 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 格式以下,左侧为每一行的标签类型,右侧为文件内容:blog
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——入门指南](http://www.jianshu.com/p/1e402922ee32/) OTHER 2. [Markdown的基本语法](http://www.cnblogs.com/libaoquan/p/6812426.html) ORDER_END OTHER OTHER ### markdown 标签分类 UNORDER_BEGIN OTHER - markdown 标签能够 **简单** 分为 2 大类:... OTHER - 其中,*代码*、`引用`、无序列表、有序列表、标题这 5 类... OTHER - 除了这 5 类标签外,图片,连接、行内引用、粗体、斜体这 5 类... UNORDER_END OTHER
在此次扫描中,能够肯定出 代码行
标签 CODE_LINE, 无序列表行
标签 UNORDER_LINE, 有序列表行
标签 ORDER_LINE, 空行
标签 BLANK_LINE, 标题行
标签 TITLE。
/** * 判断每一行 markdown 语法对应的 html 类型 * @param 空 * @return 空 */ 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——入门指南](http://www.jianshu.com/p/1e402922ee32/) ORDER_LINE [Markdown的基本语法](http://www.cnblogs.com/libaoquan/p/6812426.html) ORDER_END BLANK_LINE TITLE ### markdown 标签分类 UNORDER_BEGIN UNORDER_LINE markdown 标签能够 **简单** 分为 2 大类:... UNORDER_LINE 其中,*代码*、`引用`、无序列表、有序列表、标题这 5 类... UNORDER_LINE 除了这 5 类标签外,图片,连接、行内引用、粗体、斜体这 5 类... UNORDER_END BLANK_LINE
在此次扫描中,根据每一行的标签,将其转化为 html 代码,并行内扫描肯定 图片
、连接
、 行内引用
、 粗体
、 斜体
。
/** * 根据每一行的类型,将 markdown 语句 转化成 html 语句 * @return 空 */ 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>"); } } } /** * 将行内的 markdown 语句转换成对应的 html * @param mark 语句 * @return html 语句 */ 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>代码 (```)</code> CODE_LINE <code>引用 (>)</code> CODE_LINE <code>无序列表 ('*','-','+')</code> CODE_LINE <code>有序列表 ('1.','2.','3.')</code> CODE_LINE <code>标题 (#)</code> CODE_LINE <code>图片 (![]())</code> CODE_LINE <code>连接 ([]())</code> CODE_LINE <code>行内高亮 (`)</code> CODE_LINE <code>粗体 (**)</code> CODE_LINE <code>斜体 (*)</code> CODE_LINE <code>表格</code> CODE_END </pre> TITLE <h2>markdown 入门1</h2> ORDER_BEGIN <ol> ORDER_LINE <li><a href='http://www.jianshu.com/p/1e402922ee32/'> Markdown——入门指南</a></li> ORDER_LINE <li><a href='http://www.cnblogs.com/libaoquan/p/6812426.html'> Markdown的基本语法</a></li> ORDER_END </ol> BLANK_LINE TITLE <h3>markdown 标签分类</h3> UNORDER_BEGIN <ul> UNORDER_LINE <li>markdown 标签能够 <strong>简单</strong> 分为 2 大类:...</li> UNORDER_LINE <li>其中,<i>代码</i>、<code>引用</code>、无序列表、有序列表、标题这 5 类...</li> UNORDER_LINE <li>除了这 5 类标签外,图片,连接、行内高亮、粗体、斜体这 5 类...</li> UNORDER_END </ul> BLANK_LINE
至此,将 mdList 与 html 头部 与 尾部 写入 html 文件便可。