原样输出 (verbatim)

有时需要让输入的文字在页面上 逐字逐句原样出现:命令、注释和特殊字符的意义全部关闭,并用等宽(typewriter)字体输出。这种机制称为 verbatim。本页依次介绍行内的 \verb、块级 verbatim 环境、整文件输入的 \verbatiminput,以及“不能放在另一个命令参数中”这一重要限制和绕过方法;还会介绍保留少数命令有效的 alltt,以及可加行号和边框的 fancyvrb

verbatim 环境与 \verb

逐字输出的基础是 LaTeX 自带的 verbatim 环境。写在 \begin{verbatim}\end{verbatim} 之间的内容会按输入原样打印:反斜杠不再开始命令,而是作为 \ 输出;% 不再开始注释;空格和换行都原样保留;整个块用 typewriter 字体(\tt)排版。它适合展示程序源码,或包含许多 $& 的符号串。

latex
\begin{verbatim}
for i in range(3):
    print("100% & $5")   # nothing here is interpreted
\end{verbatim}

只有一条必须遵守的规则:环境内部不能原样出现字符串 \end{verbatim}。LaTeX 一看到这个字符串,就会把它当作环境结束。如果确实需要显示 \end{verbatim} 这串文字本身,可用后面介绍的 fancyvrb 改变终止字符串,或用 \verb 单独处理这一段。

如果只想在一行 中间 插入一点逐字内容,使用行内形式 \verb。写法是:在 \verb 后面立刻放一个分隔字符,接着写要原样输出的文字,再用同一个分隔字符结束。分隔字符可以是 任意不出现在内容中的非字母字符。常见写法是 \verb|...|,但如果内容里有 |,就换成 \verb!...!\verb+...+ 等。\verb 和分隔字符之间不能有空格。

latex
The macro \verb|\textbf{...}| sets bold text, and
the pipe itself is shown with \verb!a|b! instead.

带星号的 \verb* 会把内部空格打印成可见空格字符(␣),适合需要准确显示空格数量的代码例子。同样,verbatim* 环境也会把块内空格显示为 ␣。

不过,如果只是想处理容易包含 ~#%_ 的字符串,例如 URL,那么 urlhyperref package 提供的 \url{...} 往往更方便,因为它还会在合适位置换行。

排版整个文件(\verbatiminput)

若要原样插入外部文件内容,verbatim package(属于 LaTeX required tools)提供的 \verbatiminput{filename} 很方便。先在导言区加载 \usepackage{verbatim},再在正文中写 \verbatiminput{program.py},该文件的每一行都会按 verbatim 排版。与手动复制进稿件不同,只要编辑源文件,输出会自动跟随更新,不必手动同步代码和文档。

document.tex
\usepackage{verbatim}
% ...
\verbatiminput{hello.py}

同一个 verbatim package 还会添加 comment 环境,它会跳过 \begin{comment}\end{comment} 之间的所有内容。这不是用于逐字输出,而是用于 *完全不输出*,适合临时收起草稿段落。

不能放进命令参数中

这是 verbatim 最常见的绊脚点。\verbverbatim 环境不能出现在另一个命令的参数中,例如 \section{\verb|code|}\footnote{...}、表格单元格或 \caption{...} 内。原因在于 category code(catcode,即每个字符的角色)。\verb 会在读取参数前切换 catcode,但命令参数在命令被调用的那一刻 已经按通常 catcode 转换成 token list,因此在 \verb 来得及切换之前解释已经结束。这样写通常会报错。

有两个绕过方法。一个是 cprotect package:只要在需要保护的命令前加 \cprotect,其参数中的 verbatim 就能通过。例如写 \cprotect\section{\verb"foo"}。它还提供 \cprotEnv,用于保护环境的 \begin

另一个是后面介绍的 fancyvrb 中的 \SaveVerb / \UseVerb:先把 verbatim 文本用一个名称保存起来,在命令参数中只调用这个名称。用 \SaveVerb{label}|verbatim text| 保存,用 \UseVerb{label} 输出。

latex
% Workaround 1: cprotect
\usepackage{cprotect}
\cprotect\section{The \verb|\foo| command}

% Workaround 2: fancyvrb SaveVerb / UseVerb
\usepackage{fancyvrb}
\SaveVerb{cmd}|\foo|
\section{The \UseVerb{cmd} command}

用 alltt 保留命令

在普通 verbatim 中,即使想给代码例子的 某一部分加粗或上色,也因为命令被关闭而无法做到。alltt package(也属于标准 LaTeX 发行版)及其 alltt 环境 可以解决这个问题。allttverbatim 类似,会用等宽字体按输入排版,但 反斜杠 \ 和花括号 {} 保持通常含义。因此可以获得类似 verbatim 的外观,同时在内部使用 LaTeX 命令。

latex
\usepackage{alltt}
% ...
\begin{alltt}
def \textbf{greet}(name):
    return "Hi, " + name   \textit{# a comment}
\end{alltt}

在这个例子中,函数名 greet 变成粗体,注释部分变成斜体,其余内容都保持输入原样。代价是,如果想把 \{} 这三个字符 作为字符本身 输出,必须写成 \textbackslash\{\};而 verbatim 环境会直接输出它们。想手动加一点格式时用 alltt,想完全原样输出时用 verbatim

用 fancyvrb 添加行号和边框

内建的 verbatim 不能添加行号或边框。功能更强的是 fancyvrb package,其核心是 大写 V 的 Verbatim 环境(不同于小写的 verbatim)。选项可以按环境指定,如 \begin{Verbatim}[key=value, ...],也可以在导言区用 \fvset{key=value, ...} 全局设置。

最常用的选项如下:numbers=left(或 right)添加行号,frame=single 给整个块画边框,fontsize=\small 指定字号。另外,commandchars 可以让本来是 verbatim 的文字内部仍有少数命令保持有效。

选项常见值作用
numbersnone / left / right行号位置(默认 none)
framenone / single / lines / leftline / topline / bottomline边框类型(默认 none)
fontsize\small\footnotesize字号(默认与正文相同)
commandchars例如 \\\{\}指定 escape 字符与两个分组字符,从而启用命令

下面的例子是一个 Verbatim 块:左侧有行号,外面有单线边框,字体略小。

latex
\usepackage{fancyvrb}
% ...
\begin{Verbatim}[numbers=left, frame=single, fontsize=\small]
def greet(name):
    return "Hello, " + name
\end{Verbatim}

指定 commandchars=\\\{\} 后,在 verbatim 文本内部,\{} 分别作为 escape 字符和分组分隔符使用,从而可以像 alltt 一样嵌入命令。还有用于读入整个文件的 \VerbatimInput[options]{filename};与 \verbatiminput 不同,它可以直接接受上面的选项。此外,通过前面介绍的 \SaveVerb / \UseVerb,verbatim 也能用于命令参数中。

最后提醒一点:这里介绍的机制只负责 原样输出,并不做给关键字上色之类的 语法高亮。如果要用颜色和格式展示程序源码,更合适的是专门的 listings package,或使用 Python Pygments 的 minted;这些内容在“代码清单”页面介绍。