21 Markdown格式

21.1 介绍

Markdown是一种很简单的文本文件格式, 通常保存为.md扩展名。 Mardown中文内容应该使用UTF-8编码。 Markdown文件里面有一些简单的格式标注方法, 比如两个星号之间的文字会转化为斜体, 缩进四个空格或一个制表符的内容会看成代码。

Markdown适用于比较简单的文章、源程序说明等, 不太适用于复杂的含有大量数学公式、图表的文章, 从markdown格式比较适合转换为html(网页)格式, 也可以转换为MS Word的docx格式, 通过docx格式可以转存为PDF格式。 R扩展包knitr、rmarkdown和bookdown与pandoc软件一起大大扩展了markdown格式的适用范围。

如果需要作为一本书发布在网站上或者出版, 可考虑使用R的bookdown扩展包。 如果需要对出版格式进行精确控制, 可考虑用LaTeX格式, LaTeX格式很复杂, 学习比较困难, 但是表达能力强。

21.2 Markdown格式文件的应用

Markdown格式用在一些微博、论坛中作为缺省格式, 用户在网络浏览器软件的输入框中按照markdown格式输入, 网站自动将其转换为html富文本内容显示出来。

因为markdown格式就是纯文本, 而且其格式十分简单, 所以可以仅仅用普通文本编辑器编写markdown格式的文件, 不需要转换为其它格式。

有一些独立运行的编辑软件, 在其中输入了markdown格式文件后, 可以并列地显示转化为html富文本后的结果, 很多还支持自动备份到云服务中。 但是,这些独立运行的编辑器大都有商业因素。 比如,国内的印象笔记软件是提供了云存储的笔记软件, 在其PC端就支持创建markdown格式的笔记, 可以在输入时即时显示html效果。

RStudio软件不是专用的Markdown编辑器, 它是一个R程序的集成编辑、运行环境, 对个人用户免费, 在R Studio中可以编辑Markdown文件和含有R代码的Markdown文件, 可以一键将其转化成HTML、MS Word docx文件, 在单独安装的LaTeX编译软件支持下还可以直接编译成PDF。 RStudio支持下的增强的Mardown格式, 称为RMarkdown格式, 以.Rmd为扩展名, 支持大多数的LaTeX公式, 在bookdown扩展包支持下还支持公式、定理、图表等自动编号和引用、链接。

21.3 markdown格式说明

这部分内容参考了markdown手册和Rstudio的文档, 以及(Xie, Allaire, and Grolemund 2019)

21.3.1 概述

Markdown格式是John Gruber于2004年创造的, Markdown 的目标是实现“易读易写”。 Markdown定义了一种简单好用的文本文件格式, 作为单独的文本文件, 此格式没有什么多余的标签, 又可以转化为很多其它的格式。

Markdown 的语法全由一些符号所组成, 这些符号经过精挑细选,其作用一目了然。 比如:在文字两旁加上星号,看起来就像强调。 Markdown 的列表看起来就像我们平常在邮件中写一个列表的方法。 Markdown 的区块引用看起来就真的像是引用一段文字, 就像你曾在电子邮件中见过的那样。

需要时, 可以直接在markdown中写HTML标记内容。 markdown能实现的功能是HTML的一部分, 但是比HTML内容更干净, 没有掺杂过多的与要表达的意思无关的标签。 Markdown的理念是,能让文档更容易读、写和随意改。

21.3.2 段落

一个段落由一行或连续的多行组成。 段落之间以空行分隔。 同一段落内的不同行在转换成HTML或docx等格式后会重新排列, 原来的段内换行被当成了空格,这样的规定与LaTeX类似。 普通段落不该用空格或制表符来缩进, 不应在行尾留有空格。

为了在段内换行并且转化后仍保持段内换行, 输入时在前面行的末尾输入两个或两个以上空格。例如:

白日依山尽,黄河入海流。    
欲穷千里目,更上一层楼。

显示结果为:

白日依山尽,黄河入海流。
欲穷千里目,更上一层楼。

这样做的缺点是末尾的空格时不可见的。 可以使用HTML的<br>标签在段内换行,如:

白日依山尽,黄河入海流。<br>
欲穷千里目,更上一层楼。

结果为

白日依山尽,黄河入海流。
欲穷千里目,更上一层楼。

21.3.3 段内文字格式

在一段内,用星号或下划线包围的内容如*强调*强调格式。 用双星号或双下划线包围的内容如**加重**加重格式。 星号、下划线与要强调或加重的内容之间不要空开, 否则会当作普通星号或下划线解释, 在行首还会当作列表。 为了插入普通的星号或下划线,可以使用反斜杠保护, 或者写成段内代码格式。

可以用一对~作为界定符给出下标, 如HO~2~会变成HO2。 可以用一对^作为界定符给出上标, 如Cu^2+^会变成Cu2+。 但是,数学公式一般还是应该使用LaTeX数学公式形式(见22.8)。

在普通段落内一部分内容希望显示成代码, 对其中的特殊字符不进行解释, 只要包在两个反向单撇号内。 如`if(_x_ > 0) y=1;`会变成if(_x_ > 0) y=1;。 如果内容本身就包含反向单撇号, 可以在两边使用更多个数的反向单撇号, 如`` `x` ``会变成`x`

在Markdown文件中, 为了使得某些有特殊意义的字符不作特殊解释, 可以在该字符前面加上反斜杠\, 取消其特殊含义。

在Rmarkdown文件中, 为了原样显示反向单撇号, 可以在两边用双写的反向单撇号界定字符串。

21.3.4 标题和分隔线

以一个井号#开始的行是一级标题, 以两个井号#开始的行是二级标题, …………, 以六个井号#开始的行是六级标题。 标题行前面应该空一行, 否则可能把某些偶然出现在行首的#号误认为标题行的标志。

对一级标题, 也可以用标题内容下面输入一行等于号=表示上一行内容是一级标题。 对二级标题, 可以用标题内容下面输入一行减号-表示上一行内容是二级标题。 等于号和减号的个数不限。

用三个或三个以上连续的星号组成的行, 可以转换成分隔线。下面是一个分隔线:


21.3.5 引用段落

可以用类似Email的回复包含原始邮件内容的办法输入引用段落, 即,在段落的每行前面加一个大于号。 比如下面的诗:

> 白日依山尽,黄河入海流。
> 欲穷千里目,更上一层楼。

转换成

白日依山尽,黄河入海流。 欲穷千里目,更上一层楼。

注意引用也是段落模式,内容中的换行不起作用,空行导致分段。

引用段落也可以仅在段落第一行写大于号, 其它行顶格写,例如下面的两段引用:

> 远上寒山石径斜,
白云生处有人家。
>
> 停车坐爱枫林晚,
霜叶红于二月花。

转换成

远上寒山石径斜, 白云生处有人家。

停车坐爱枫林晚, 霜叶红于二月花。

引用也可以嵌套,如:

> 张三说:李四这样说过
>
>> 不想当将军的木匠不是好厨子。
>

转换成

张三说:李四这样说过

不想当将军的木匠不是好厨子。

注意嵌套内容前后都有空的引用行,否则不能实现嵌套引用。

引用内也可以嵌套其它的Markdown格式如标题、列表等。 引用前后应该有空行把引用内容与其他内容分隔开。

为了在引用中换行, 就需要加引用空行。 为了排版诗、词之类的内容, 希望人为控制换行和引导空格, 可以将引用中的>替换成|,如:

| 白日依山尽,
| 黄河入海流。
| 欲穷千里目,
| 更上一层楼。

转换成

白日依山尽,
黄河入海流。
欲穷千里目,
更上一层楼。
| 枯藤老树昏鸦,
|   小桥流水人家,
|   古道西风瘦马。
| 夕阳西下,
|   断肠人在天涯。

转换成:

枯藤老树昏鸦,
  小桥流水人家,
  古道西风瘦马。
夕阳西下,
  断肠人在天涯。

其中不用|开头的行仍会当作上一行的续行, 不会强行换行。

21.3.6 列表

列表分为不编号的列表和编号的列表。 不编号的列表转化后通常显示圆点开头的列表项。

在Markdown中, 用星号表示一个不编号的列表项。 星号也可以替换成加号或减号, 后面必须有一个或多个空格。 每个列表项可以输入多行, 各行的内容最好左对齐, 左对齐在使用文本格式时较易阅读, 但不是必须的。 两个列表项之间不要空行。 例如:

* 白日依山尽,
  黄河入海流。
* 欲穷千里目,
  更上一层楼。

转换为

  • 白日依山尽, 黄河入海流。
  • 欲穷千里目, 更上一层楼。

段落顶头的数字加句点和空格表示编号列表, 两个列表项之间尽量不要空行。 例如:

1. 第一种解决方法,
   收买敌人的高官。
2. 第二种解决方法,
   尽可能拖延。

转换为

  1. 第一种解决方法, 收买敌人的高官。
  2. 第二种解决方法, 尽可能拖延。

标准的markdown编号列表不能自己定义数字的显示格式, 不允许开始值不等于1。 pandoc支持更自由的列表, 允许输入时有括号或右括号,允许使用字母和罗马数字,但是括号会被去掉。如

(1) 顶层一;
    a) 内层二;
    b) 内层三;
(2) 顶层二。

转换为

  1. 顶层一;
    1. 内层二;
    2. 内层三;
  2. 顶层二。

为了避免错误地产生非本意的编号列表, 在行首写数字加句点和空格时,可以在句点前加反斜杠, 或者在句点前加空格。例如,下面是一个年号:

2016\. 

如上输入将不会错误地解释为有序列表。

如果列表项目中有多个段落, 这时两个列表项之间应该以空行分隔, 每个项目除了第一行外,输入的每行内容都应该缩进4个空格或者一个制表符。 例如:

*   R语言第一个版本开发于1976-1980,基于Fortran;
    于1980年移植到Unix, 并对外发布源代码。
    1984年出版的“棕皮书”
    总结了1984年为止的版本, 并开始发布授权的源代码。
    这个版本叫做旧S。与我们现在用的S语言有较大差别。

    1989--1988对S进行了较大更新,
    变成了我们现在使用的S语言,称为第二版。
    1988年出版的“蓝皮书”做了总结。

*   1992年出版的“白皮书”描述了在S语言中实现的统计建模功能,
    增强了面向对象的特性。软件称为第三版,这是我们现在用的多数版本。

    1998年出版的“绿皮书”描述了第四版S语言,主要是编程功能的深层次改进。
    现行的S系统并没有都采用第四版,S-PLUS的第5版才采用了S语言第四版。

转换为

  • R语言第一个版本开发于1976-1980,基于Fortran; 于1980年移植到Unix, 并对外发布源代码。 1984年出版的“棕皮书” 总结了1984年为止的版本, 并开始发布授权的源代码。 这个版本叫做旧S。与我们现在用的S语言有较大差别。

    1989–1988对S进行了较大更新, 变成了我们现在使用的S语言,称为第二版。 1988年出版的“蓝皮书”做了总结。

  • 1992年出版的“白皮书”描述了在S语言中实现的统计建模功能, 增强了面向对象的特性。软件称为第三版,这是我们现在用的多数版本。

    1998年出版的“绿皮书”描述了第四版S语言,主要是编程功能的深层次改进。 现行的S系统并没有都采用第四版,S-PLUS的第5版才采用了S语言第四版。

列表项目内如果有引用段落, 需要都缩进4个空格。 如果有程序代码, 需要缩进4个空格后用三个反单撇号表示开始与结束。

列表可以嵌套, 嵌套的列表需要缩进4个空格, 中间不需要空行。 例如:

1.  第一类工作包括:
    + 技术服务;
    + 咨询服务。
2.  其它工作略。

转换为

  1. 第一类工作包括:
    • 技术服务;
    • 咨询服务。
  2. 其它工作略。

如果需要把每个列表项当作段落排版,可以在每个列表项后空行。

21.3.7 源程序

为了让源程序能够自动显示成源程序的样式, 而不至于自动分行、特殊字符解释, 用空行把源程序与其它内容隔开, 并把源程序行都缩进4个空格(或以上)或者一个制表符。 源程序格式持续到不缩进4个空格的地方为止。

例如,下面的输入:

    f <- function(x){
        n <- length(x)
        y <- numeric(n)
        y[x >= 0] <- 1
        ##y[x < 0] <- 0
        
        y
    }

转换为

f <- function(x){
    n <- length(x)
    y <- numeric(n)
    y[x >= 0] <- 1
    ##y[x < 0] <- 0
    
    y
}

更适当的做法是用三个连续的反向单撇号表示代码开头与代码结束, 中间就会当作源程序代码处理。 例如下面的输入

```
> x <- rnorm(100)
> hist(x)
```

转换为

> x <- rnorm(100)
> hist(x)

R的knitr包在Markdown格式的文件中插入R可执行代码时, 就用了这样的方法。 而且,R Markdown格式的代码块不需要用空行与前后分隔开。

在pandoc程序的支持下, 代码段还可以采用栅栏式代码段, 在代码段开头前面一行加上至少三个连续~符号, 在结尾后面一行加同样数目的~符号。 这样的代码段前后也必须空行以与其它内容分开。 另外,如果代码内本身含有~行, 只要使得开头与结尾标志中的~个数更多就可以了。 例如下面的输入

~~~
#include <math.h>
double sqr(double x){
  return(x*x);
}
~~~

转换为

#include <math.h>
double sqr(double x){
  return(x*x);
}

使用栅栏式代码段时可以在开始行尾写大括号, 在大括号内写选项。 其中一种选项是要求按照某种编程语言对结果进行彩色语法显示, 如.cpp表示C++,.c表示C,.r表示R,.python表示python等。 选项.numberLines要求该代码行编号, 选项startFrom=指定开始行号。 如:

~~~{.cpp .numberLines startFrom=101}
#include <math.h>
double sqr(double x){
  return(x*x);
}
~~~

转换为

#include <math.h>
double sqr(double x){
  return(x*x);
}

21.3.9 插入图形

图形只能用链接形式, 不可能保存到一个纯文本文件内。 图形文件可以存在于远程服务器上, 也可以是与生成的HTML文件在同一目录结构中的文件。 语法仍使用行内式和参考式两种形式。 转化成HTML、PDF、Word格式后可以把图形内嵌在输出文件内部。

行内式的图片链接, 是普通行内链接格式前面添加了一个叹号, 惊叹后面紧接着方括号, 方括号内写图片的标题,标题可以空缺, 在右方括号后面紧接着圆括号, 圆括号内写图片的链接。

例如,下面的代码可以插入百度的一个logo,使用的是网上的资源:

![](http://www.baidu.com/img/baidu_jgylogo3.gif)

结果为

为了插入保存在本地的与Markdown源文件在同一子目录或下级子目录的图形, 只要在圆括号中写图片文件名(如果与Markdown源文件在同一子目录)或相对路径。 例如,在D:\work\figs下的图形文件baidu_logo.gif在本Markdown源文件所在目录的figs子目录中, 可以用代码

![](figs/bd_logo.png)

结果为

这样含有网上图片和本地图片的Markdown源文件转化为HTML和docx格式, 都可以正常显示插入的图片。

与链接类似, 也可以在文章某处(比如末尾)定义图片的标识符, 然后把行内图片引用中图片地址替换成图片标识符即可。

21.3.10 表格

Markdown文本格式的表格就像是用减号、等号、竖线画的文本格式表格一样, 转化为HTML、docx等格式后就变成了富文本的表格。 有如下几种表格:

  • 管道表
  • 简单表
  • 换行表
  • 有格表

21.3.10.1 管道表

管道表在两列之间用竖线分开, 在列标题下面用减号画横线, 用如下方法指定各对齐方式:

  • 在列标题下的横线开始加冒号,表示左对齐;
  • 在列标题下的横线末尾加冒号,表示右对齐;
  • 在列标题下的横线两端加冒号,表示居中对齐;
  • 列标题下面仅有横线没有冒号,表示缺省对齐方式,一般是左对齐。

在表格内容后面空一行后写用Table:开头的表格说明。

这种方法不需要输入内容上下对齐,适用于中文内容。 后面所讲的简单表、换行表、有格表需要能够输入内容对齐, 对于中英文混合内容很难做到对齐, 所以仅管道表比较适合中文内容。

例如:

| 姓名   |  收入      |    职业          | 颜色偏好  |
|:------|-----------:|:---------------:|-----------|
| 赵四海  |    123456  |   业务经理      |   红      |
| 刘英    |        50   |     无        |   蓝      |
| 钱德里  |      3200   |    保洁        |    灰     |

Table: 管道表示例

结果为

管道表示例
姓名 收入 职业 颜色偏好
赵四海 123456 业务经理
刘英 50
钱德里 3200 保洁

管道表不允许输入单元格换行, 单元格内容太宽时转换结果可能自动换行, 自动换行时列宽度与输入的列标题下横线宽度成比例。

21.3.10.2 简单表

简单表的格式是, 第一行是各列标题, 第二行是各标题下面用减号组成的表格线, 同一行的不同列要用空格分开, 从第三行开始是内容。

在表格前或表格后用空行隔开的以Table:开头的行是表格说明或标题。

为了确定表格每列单元格内容如何对齐, 用列标题下的表格线给出提示:

  • 表格线与列标题右对齐,表示该列右对齐;
  • 表格线与列标题左对齐,表示该列左对齐;
  • 列标题在表格线中间,表示该列居中对齐;
  • 列标题左右都与表格线对齐,表示该列为缺省对齐方式,一般是左对齐。

一定要使用一个等宽字体来编辑这样的表格,否则对齐与否无法准确分辨。 单元格内容不能超出表格线左端。 经过试验发现, 中文内容很难按这种方法对齐。

例如:

Name        Income          Job              Color
------    --------     ------------------    -----
Jane        123456     Research Assistant    red 
John           50        N/A                 blue
William      3200      Cleaner               blue

Table: 一个简单表的例子

结果为:

一个简单表的例子
Name Income Job Color
Jane 123456 Research Assistant red
John 50 N/A blue
William 3200 Cleaner blue

21.3.10.3 换行表

换行表在输入列标题和单元格内容时, 允许输入内容拆分行,但是转化后并不拆分行。 这样的表以一行减号开始,以一行减号结束, 中间的表格用空行分开实际的不同行。 例如:

----------------------------------------------------
Name                     
of
Subject     Income        Job                color           
------    --------     ------------------    -----
Jane        123456       Research            red 
Ayer                     Assistant

John           50        N/A                 blue
Tukey

William      3200      Cleaner               blue
Tale
----------------------------------------------------

Table: 一个换行表的例子

结果为:

一个换行表的例子
Name of Subject Income Job color
Jane Ayer 123456 Research Assistant red
John Tukey 50 N/A blue
William Tale 3200 Cleaner blue

换行表输入时各列的输入宽度是有作用的, 输入较宽的列结果也较宽。

21.3.10.4 有格表

完全用减号、竖线、等于号、加号画出表格线。 这样的表在文本格式下呈现出很好的表格形状。 转化后不能指定对齐方式。

例如:

Table: 有格表示例

+---------------+---------------+--------------------+
| Fruit         | Price         | Advantages         |
+===============+===============+====================+
| Bananas       | $1.34         | - built-in wrapper |
|               |               | - bright color     |
+---------------+---------------+--------------------+
| Oranges       | $2.10         | - cures scurvy     |
|               |               | - tasty            |
+---------------+---------------+--------------------+

结果为

有格表示例
Fruit Price Advantages
Bananas $1.34
  • built-in wrapper
  • bright color
Oranges $2.10
  • cures scurvy
  • tasty

21.4 附录:pandoc软件介绍

pandoc是一个杰出的开源软件, 作者为John MacFarlane。 RStudio软件中已经包含了这个软件。 此软件在操作系统命令行运行, 可以用来在多种不同的文件格式之间进行转换, 输入格式一般是文本格式, 比如markdown、LaTeX、MediaWiki、reStructured Text、 HTML、DocBook, 但是也可以输入MS Word docx、Open Office ODT、EPUB格式。 输出可以是这些文本格式, 也可以是MS Word docx、Open Office ODT、EPUB、LaTeX等格式, 在安装了LaTeX编译系统如MikTeX时可以输出为PDF。

可以用普通文本编辑器编写markdown格式的文件, 用pandoc转换为HTML或MS Word docx等格式。 因为pandoc需要UTF-8编码的输入文件, 所以应该把markdown文件保存为UTF-8格式, MS Windows下的Notepad++软件可以很容易地编辑文本文件 并在各种编码之间转换。

首先, 从pandoc网站下载并安装pandoc。 安装程序很奇怪地安装到了 C:\Users\登录用户名\AppData\Local\Pandoc中, 请将此子目录复制到一个合适的位置, 比如C:\Pandoc。 如果把此路径加入到Windows系统的可执行文件搜索路径中 (在“控制面板-系统和安全-系统-高级系统设置-环境变量”中, 为系统变量的Path添加一个分号分开的路径C:\Pandoc即可以不用全路径访问 pandoc.exe可执行文件。

为了用pandoc转化某个markdown文件, 首先在该文件所在子目录打开一个Windows命令行窗口, 在MS Win10系统中只要在文件管理器的“文件”菜单选择 “打开Windows PowerShell”即可。 比如,文件名是test.md, 用如下pandoc命令可以转换为.docx文件(MS Word文件的新版本):

C:\Pandoc\pandoc -o test.docx test.md 

用如下pandoc命令可以转换为.html文件:

C:\Pandoc\pandoc -s -o --mathjax test.html test.md

如果把pandoc.exe加入了Windows操作系统的Path环境变量中, 上面的 C:\Pandoc\pandoc 可以简写为 pandoc

pandoc在其它操作系统中也有相应的版本。 软件下载与文档见Pandoc的网站http://pandoc.org

如果使用RStudio编辑markdown文件或者R Markdown文件, 它会自动调用内置的pandoc程序。


References

Xie, Yihui, J. J. Allaire, and Garrett Grolemund. 2019. R Markdown: The Definitive Guide. CRC Press. https://bookdown.org/yihui/rmarkdown/.