markdown语法详解
说明
尽管基础的 markdown 语法很简单,但是由于解析器实现的不一致性,尤其是对于扩展的 markdown 语法的支持程度差异很大,导致我们在使用 markdown 的时候会出现不同场景的差异性。这篇文章打算以 GFM 和 Typora 为主,去介绍一下 markdown 的语法格式规范以及各个解析器的支持程度。
本文中涉及到的所有的 markdown 语法我都会尽量注明各种情况是否支持(没有特别指出的时候,默认都支持),同时也会加一些markdownlint的规则描述。
- GFM:GitHub Flavored Markdown,算是最通用的 markdown 扩展规范了;
- Typora:个人认为最好的 markdown 可视化编辑器和查看器,支持
windows
和mac
,在 GFM 基础上,它还支持很多额外的 markdown 扩展; - kramdown:作为
jekyll
的默认 markdown 解析器,现在应该是 github pages 默认的 markdown 解析器; visual studio code
的 markdown 扩展:可以方便启用markdownlint
检查,尽管该解析器还存在较多问题,也会关注下其解析效果;
基础语法
标题
使用#
字符就可以指定标题,输入 1-6 个#
,分别对应 1-6 级标题。顾名思义,#
数目越少,标题级别就越高。#
和标题名之间应该确保包含一个空格,另外标题前后需要存在一行空行。
# 一级标题
## 二级标题
### 三级标题
###### 六级标题
在生成html
时,会对应到<h1>
、<h2>
、<h3>
和<h6>
<h1>一级标题</h1>
<h2>二级标题</h2>
<h3>三级标题</h3>
<h6>六级标题</h6>
正文段落
普通文本可以直接输入,需要注意的是普通的换行是不生效的(也有部分 markdown 解析器会生效),只是方便在编辑器中查看而已。如果要实现换行的话,需要在行尾加上两个空格,或者手动加上<br/>
标签(不符合 markdown lint 标准,非必要情况下不要这么操作)。
我在上面末尾加了两个空格,所以这是新的一行(但不是新的一段)。
如果中间有一行空行的话,则会产生是一个新的段落。换行对应到 html 的<br/>
,段落对应到 html<p> </p>
。
行首和行尾的单个空格一般会被忽略,段落之间不要有多行空行。
字体样式
字体样式 | 语法 | 例子 | html |
---|---|---|---|
粗体(Bold) | ** ** 或__ __ |
这是粗体 | <strong></strong> |
斜体(Italic) | * * 或_ _ |
这是斜体 | <em></em> |
粗斜体(Bold and Italic) | *** *** 或___ ___ |
这是粗斜体 | <strong><em></em></strong> |
删除线(Strikethrough) | ~~ ~~ |
<del></del> |
删除线不属于标准的 markdown 规范,但是一般的解析器都会支持。至于使用*还是使用_则看个人习惯,使用*的人更多一些。
引用
使用>
即可进行引用,对应 html 的<blockquote></blockquote>
,如果引用需要分多段的的话,可以在中间加一个仅包含>
的一行即可。如果中间是一个空行的情况,大部分解析器会将其认为是 2 个引用块,也有部分会将其认为是 1 个引用块,使用中最好尽量避免中间有空行的情况出现。
李白曾经写过
君不见,黄河之水天上来,奔流到海不复回。君不见,高堂明镜悲白发,朝如青丝暮成雪。
人生得意须尽欢,莫使金樽空对月。天生我材必有用,千金散尽还复来。
列表
使用*
、-
或+
产生无序列表,使用数字加.
产生有序列表,有序列表的数字不一定需要有序,但是需要从 1 开始,也可以所有都是 1。
无序列表:
- Java
- C/C++
- Python
有序列表:
- Java
- C/C++
- Python
- Go
- PHP
无序列表产生的 html 如下<ul><li></li><li></li></ul>
,有序列表产生的 html 如下<ol><li></li><li></li></ol>
对于多级列表,需要确保子列表与上一级列表的内容对齐,对于有序列表的二级列表缩进 3 个空格(数字+.
+空格),对于无序列表的二级列表缩进 2 个空格(-
+空格)。对于 list 内部包含段落、引用、代码块、表格等情况,建议和上述规则保持一致。对于上述情况在二级列表或者段落前后包含一个空行。
不同的 markdown 解析器对于列表内包含段落的情况处理的并不一致,所以存在着各种写法,比如段落前用 4 个空格、在上一行末尾加入 2 个空格(软换行)等等,建议保持标准的写法兼容尽量多的解析器。
1. Java
Java 连续霸榜 TIBOE 编程语言排行榜,已然成为业界最受欢迎的变成语言。
2. C/C++
- C
- C++
3. Python
4. PHP
> 曾经有人说 PHP 是世界上最好的语言
LAMP 曾经是标配,如今已经不再那么流行……
-
Java
Java 连续霸榜 TIBOE 编程语言排行榜,已然成为业界最受欢迎的变成语言。
-
C/C++
- C
- C++
- Python
-
PHP
曾经有人说 PHP 是世界上最好的语言
LAMP 曾经是标配,如今已经不再那么流行……
链接
链接写法 [展现名](链接地址 "标题")
,比如腾讯网,其中的"标题"
可以省略,链接地址可以使用绝对路径也可以使用相对路径,或者指向本地/本网站的其他文件。更进一步,也可以指向本文件的其他锚点(书签),比如跳转引用。
展现的名字和链接地址一致的时候会显得有些冗余,比如[https://www.qq.com](https://www.qq.com)
,有更简洁的写法,直接用<https://www.qq.com>
生成https://www.qq.com。
如果多处需要链接相同的地址,我们也可以采用引用的方式,使用[展现名][链接名]
,然后在任意地方定义链接名的具体地址 [链接名]: 链接地址
,比如腾讯,这样子还有个好处是方便管理。
图片
和链接的语法类似,图片需要在前面加上!
,常见的写法![展现名][图片地址]
,如果需要图片本身也是链接的话,在外层加上链接地址:[![展现名][图片地址]](链接地址)
表格
表格不是标准的 markdown,但是常见的解析器会支持,写法如下,完整的情况会在第一列前面和最后列后面加上|
,表头和内容中间需要一行分隔符,这里建议保持和列数一致,某些解析器也会支持最简单的写法---|---
(不管多少列的情况下),但是很多解析器并不支持,这里可以通过:
来指定表格的对齐方向,左对齐、右对齐还是居中。
表头1|表头2|表头3 :---|:---:|---: 内容|内容|内容 内容|内容|内容
国家 | 面积 | 人口 |
---|---|---|
中国 | 960 万平方公里 | 14 亿 |
美国 | 936 万平方公里 | 3 亿 |
俄罗斯 | 1709 万平方公里 | 1.4 亿 |
分隔符
使用---
或者***
作为分隔符
转义字符
由于一些符号在 markdown 中有了特殊含义,比如我们就是要输入*abc*
,并不希望出现斜体的 abc,那么可以使用\
作为转衣符,输入\*abc\*
,结果为*abc*。
扩展语法
代码
行内使用`作为起始和终止符,比如printf
,代码块使用位于独立行的```作为起始和终止
int main(int argc, char *argv[])
cout << "hello world!" << endl;
return 0;
}
任务列表
任务列表(Task List)在 github 中使用非常广泛,语法格式如下,使用x
标识为已完成,未完成的情况中间需要包含空格。
- [x] 银行存款超过 1000 万
- [ ] 当上总经理
- [ ] 赢取白富美
- 银行存款超过 1000 万
- 当上总经理
- 赢取白富美
数学公式
数学公式块,使用位于独立行的$$
作为起始和终止,比如:
$$
f(x)=\sum^{\infty}_{n=0}\frac{f^{(n)}(a)}{n!}(x-a)^n
$$
行内数学公式,直接使用$
作为起始和终止:$\delta=b^2-4ac$(有些解析器需要使用$$
作为起始和终止)。
html 标签
可以使用 html 标签实现 markdown 不支持的功能,正常情况下尽量避免使用 html 标签。
- 在表格内部需要换行的时候,可以加上
<br/>
标签; - 需要指定样式的时候,可以加上类似
<span style="color:red"> </span>
标签,比如:我是红色; - 指定下划线,使用
<u> </u>
,比如:我是下划线;
非通用语法
内容目录
使用[TOC]
生成自动目录。Typora 支持该语法,GFM 不支持该语法。
时序图
使用js-sequence渲染
罗密欧->朱丽叶: 哈喽
朱丽叶-->罗密欧: 我想你了
罗密欧->>朱丽叶: 我也想你了
gfm 不支持时序图,Typora 支持。
流程图
使用flowchart.js渲染
st=>start: 开始
cond=>condition: 有房有车
op1=>operation: 赢取白富美
op2=>operation: 走向人生巅峰
e=>end: 结束
st->cond
cond(yes)->op1->op2->e
cond(no)->e
gfm 不支持流程图,Typora 支持。
mermaid 图
mermaid
相比时序图和流程图来说,功能会更强大,支持时序图、流程图、UML 图、状态图、甘特图等,Typora 支持但是 GFM 不支持。目前来看,使用算是比较广泛了。官网提供了较丰富的例子,Mermaid Live Editor提供了在线编辑以及导出 SVG。
看下用mermaid
来画时序图的例子:
sequenceDiagram
罗密欧->>朱丽叶: 哈喽
朱丽叶-->>罗密欧: 我想你了
罗密欧->>朱丽叶: 我也想你了
脚标和上下标
可以使用语法 [^脚标A]: 这是脚标A
来创建角标(需要使用代码块模式),在需要引用的地方使用[^脚标A]
进行引用[^脚标a]。Typora 支持该语法,GFM 不支持该语法。
上标和下标没有一致的标准,在 Typora 中使用^文字^
表示上标,使用~文字~
表示下标,但我看并没有得到广泛支持。建议在 markdown 中尽量避免使用上下标,如果要保持兼容性的话采用 HTML 的<sup>文字</sup>
展示上标,使用<sub>文字</sub>
的方式展示下标。
YAML 头信息
在文件的头部使用使用独立行的---
作为开始和终止,其中间部分会作为 metadata,并不会生成可视内容,这本来是jekyll的特殊格式,现在 Typora 也支持该语法(尽管不一定会生成 html 里面的 metadata)。
Github emoji
按文本形式输入类似:smile:
,常见的可以直接输入,完整的可以参考列表。Typora 和 github 都支持,vscode 的相关插件不支持。
- 人::boy::girl::man::woman::baby::older_woman::older_man::princess::cop::angel::couple::walking::runner::dancers:
- 动物::cat::dog::pig::frog::cow::horse::snake::bird::mouse::wolf::monkey::camel:
- 表情::smile::cry::confused::sob::joy::mask::worried::wink::relaxed::grin::kissing::open_mouth::heart_eyes: