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 文件便可。