LaTeX教程(015)- LaTeX \LaTeX LATEX文档结构(15)
2.4 管理引用
LaTeX \LaTeX LATEX有一些命令能够很容易的管理文档中的引用。它支持交叉引用(cross-references,文档内部对象的引用),文献引用(bibliographic,对外部文档的引用),以及对所选的文字或表达式的索引(indexing)。索引和文献引用我们在后面讲解,这里先讲交叉引用。
要使用交叉引用,你需要对一个结构上的对象指派一个"key"(一个字符串,由字母,数字和标点符号组成),用来指向那个对象的位置。
\label{key}
\ref{key}
\pageref{key}
\label
命令用来为当前的"活动对象"指派一个key。\ref
命令放置一个用来区分给定对象(如章节,公式或者图号等,具体是哪个对像,取决于放置\label
的时候哪个对象是活动的)的字符串。\pageref
命令将\label
命令所在的页的页码输出在文档中。每一个\label
所声明的key
是唯一的,不可重复。一个有用的习惯是在key
中放置一个能够区分对象类型的前缀,如sec
,可以作为章节划分命令的key
,fig
可以作为图片的key
,等等,我们做个演示:
\documentclass{article}
\usepackage[a5paper,margin=1.5in]{geometry}
\begin{document}
\section{A Section}
\label{sec:this} % 在一个section对象下面插入一个key,sec:this
A reference to this section looks
like this: ``see section~\ref{sec:this} % 将上面的section编号输出到此处
on page~\pageref{sec:this}''. % 将上面的section编号所在的页码输出到此处
\end{document}
编译:
注意,使用前缀的习惯是为了增加代码的可读性,字符串如何定义,都不能决定我们要引用的是哪个对象,只有\label
放置的位置才能决定。
那么,如何才能将\label
放置在正确的位置呢?对于划分命令,如\chapter
,\section
等; 环境,如equation
,figure
,table
,theorem
,和列表环境enumerate
的各个层级,以及脚注命令\footnote
等,都会设置一个引用字符串,该字符串包含当前对象的编号,就像上句例子中的"1",它将放置于我们使用\ref
命令的地方。这个字符串在一个对象的开始时生成,在结束时重置。也就是说,第一个section的引用字符串就是1,当前section结束时,就不再是1了。因此,我们要引用这些对象,就要将key声明在它们的作用区域内。
上述规则对table
和figure
环境是例外,它们的索引字符串是由\caption
命令定义的。这就允许一个环境中可以放置很多对\caption
和\label
命令。因为编号是由\caption
生成的,所以\label
要跟在\caption
命令的后面,否则就会生成错误的编号。如果\label
放置\caption
的前面,\label
就会捕捉到前一个对象的索引字符串,典型的情况就是当前的章节单位对象。
那么,如果多个不同的对象的作用范围是相互嵌套的呢?这种情况下我们以当前\label
所在的最小(最近)作用范围为准。例如,在一个section
中放置一个subsection
,那么在进入subsection
之前放置的\label
,会捕捉到section
的索引字符串。在进入subsection
之后放置的\label
,会捕捉到subsection
的索引字符串。如果在subsection
中放置一个图片环境,那么在进入图片环境后,在\caption
之前放置的\label
同样会捕捉subsection
的索引字符串,在\caption
后的则会捕捉图片环境的索引字符串。直到图片环境结束,则又会回到subsection
中。
然而,有些对象的作用范围可能会受到一些因素的干扰,我们作个演示:
\documentclass{article}
\usepackage[a5paper,margin=1in]{geometry}
\begin{document}
\section{A section}
\section{A section}
\section{A section}
\subsection{A subsection}\label{sec:before}
Text before is referenced as `\ref{sec:before}'.
\begin{figure}[ht]
\label{fig:in1} % 有问题的
\begin{center}
\fbox{\ldots{} figure body \ldots}
\caption{First caption}
\label{fig:in2} % 好的
\bigskip
\fbox{\ldots{} figure body \ldots}
\caption{Second caption}
\label{fig:in3} % 好的
\end{center}
\label{fig:in4}% 有问题的
\end{figure}
\label{sec:after} % 有问题的,除非你想要的是页码
\raggedright
The labels are: `before' (\ref{sec:before}),
`fig:in1' (\ref{fig:in1}) -- bad, `fig:in2' (\ref{fig:in2}),
`fig:in3' (\ref{fig:in3}), `fig:in4' (\ref{fig:in4}) -- bad
and `after' (\ref{sec:after}) -- probably bad!
\end{document}
编译:
只有fig:in2
和fig:in3
处的\label
捕捉到的索引字符是我们想要的,而fig:in4
不是。可以看出,是center
环境限制了\caption
生成的索引字符的作用范围,于是我们捕捉到了subsection
的索引字符。
注意,在浮动体中使用center
环境并不是一个好的习惯,不仅仅是因为它会限制索引的范围,还因为它会在浮动体的顶部生成一段额外的间距。更好的方法是使用\centering
来避免这两个问题。
每当声明一个\label{key}
,
LaTeX
\LaTeX
LATEX就会记录当前的索引目录和当前的页码。如果一节(section)很长,跨很多个页,此时,如果我们使用\label
将不同的key标记在这一节的不同的位置,那么它们捕捉到的节索引字符是相同的,但是页码可能不同。
特殊的标记
一个索引由\ref
产生,默认情况下,它产生的数据和相对应的\label
命令有关,通常是一个编号。通常所有额外的排版都必须由用户自己提供。例如,默认情况下(就像上一个例子那样),subsection
3.1的索引字符串就是3.1,如果你想生成像"第(3.1)小节"这样的索引字符串,(在前面的例子中)可以编写代码第(\ref{fig:in1})小节
。其他对象也是一样的。
amsmath
(一个用来写数学公式的包)提供了一个\eqref
用来生成公式的索引字符串,它会在公式的编号外面放置一对括号。如果想要在一个编号的前后自动添加一些我们想要的字符串,如equation
,可以使用\labelformat
命令。也可以使用cleveref
包,该包我们在2.4.2会讲。
\labelformat{counter}{formatting-code}
\Ref{label}
labelformat
命令有两个参数,第一个是我们要索引的对象的计数器,第二个是它的样式。第二个参数应当包含一个#1
用来接收要排版的编号。
\labelformat
有一个副作用(对中文用户不影响),对英文用户来说,我们通常希望一个句子的第一个单词的首字母是大写的,而\ref
生成的索引字符串,除编号以外都是确定的,例如,我们用\labelformat
为section
的索引定制了一个section xx
的样式,如果在一句话中,这个索引出现在一个句子的开头呢?此时我们希望这个用Section xx
代替section xx
。此时我们就可以使用\Ref
,它将索引字符串中了第一个单词的首字母大写。我们用一个例子演示:
\documentclass{article}
\usepackage[a5paper,margin=1in]{geometry}
\usepackage[nospace]{varioref}
\labelformat{section}{section~#1}
\labelformat{equation}{equation~(#1)}
% ~生成一个空格,和普通的空格不同的是,它使LaTeX不在此处断行
\begin{document}
\section{An example}\label{sec}
\Ref{sec} shows the use of the \verb=\labelformat=
declaration with a reference to \ref{eq}.
\begin{equation}
a = b \label{eq}
\end{equation}
\end{document}
编译:
要使\Ref
正常工作,那么你定制的索引最好是以字母开头的,否则可能无法转换成大写,更糟糕的情况是可能会报错。当然,如果定制成中文则不需要使用\Ref
了,也就不需要担心这个问题了。在一些语言中,字母还会带有重音符号,对这些情形也有一些调整措施,然而绝大多数的中文用户都不需要考虑这个问题,这里不再展开讲,确有需要的读者可以翻阅原著。
默认情形下,脚注(footnote)的索引字符串就是一个编号(默认是自然数,如1,2,3等),并且在report
和book
的文档类中,每开始新的一章,脚注的编号也会重置。那么,如果我文档中看到一个footnote x的索引,我如何知道它指向的是当前章的第x个脚注还是其他的某章的第x个脚注呢?
我们可以在通过\labelformat
设置,使得\label
捕捉索引字符串的时候,将章的编号信息一并捕捉到,例如:
\documentclass{book}
\usepackage[a5paper,margin=1in]{geometry}
\labelformat{footnote}{footnote~(#1)in chapter~\thechapter}
\begin{document}
\chapter{chapter one}
some text \footnote{A Footnote\label{f:a}}
\chapter{chapter two}
some text\dots
\ref{f:a}
\end{document}
编译(只截取部分):
或者,我们也可以添加一个判断,实现如果引用的脚注在来自前章时,直接输出脚注编号,而如果来自其他章时,则将其所在章的编号信息一同输出出来:
\documentclass{book}
\usepackage[a5paper,margin=1in]{geometry}
\usepackage{ifthen,varioref}
\labelformat{footnote}{footnote #1\protect\iscurrentchapter{\thechapter}}
\newcommand\iscurrentchapter[1]{%
\ifthenelse{\equal{#1}{\thechapter}}{}{ in Chapter~#1}}
\begin{document}
\chapter{chapter one}
some text \footnote{A Footnote\label{f:a}} \ref{f:a}
\chapter{chapter two}
some text\dots
\ref{f:a}
\end{document}
编译:
ifthen
包提供的\ifthenelse
命令用来进行判断,这个例子中,我们用它判断\label
捕捉到的\thechapter
信息和当前的章的\thechapter
是否一致。而varioref
则是我们接下来要讲的包。
2.4.1 varioref
-更灵活的交叉引用
很多时候,在引用图形或者表格环境时,将\ref
或者\pageref
都用上,是个很有用的习惯,因为,例如一个表格对象,可能跨很多页,我们最好不仅能定位到这个对像,也要知道我们要引用的部分在哪一页。
很多人可能会定义一个这样的命令:
\newcommand\fullref[1]{\ref{#1} on page~\pageref{#1}}
来将这两种引用合并到一个命令中。然是,我们在编写文档时,无法总是确定所引用的对象最终会落在哪一页(尤其是浮动体),所以可能会出现引用到当前页的情形。由Frank Mittelbach
编写的varioref
包试图自动解决这个问题。该包提供了命令\vref
和\vpageref
来处理单个引用,也提供了命令\vrefrange
和\vpagerefrange
来处理多个引用。
我们总是建议你在调用varioref
的时候指定nospace
包选项,如果不指定,varioref
就会操作它的命令前面的空格(如果没有空格,它还会自己加上一个),这可能会导致一些问题,应当避免它。更多相关的细节后面会讲。
现在我们讲一下\vref
和\Vref
及其星号变体的命令的形式。
\vref[same-page]{key}
\Vref[same-page]{key}
\vref*[same-page]{key}
\Vref*[same-page]{key}
当引用(\ref
)和被引用的对象(\label
)在同一页,并且不指定选项时,\vref
和\ref
命令的表现是一样的。那么,如果引用和被引用的对象在不同页呢?
在讲这个之前,我们讲一个实用的包,hyperref
可以将引用字符串设置成超链接。可以设置特定的颜色,并且可以跳转。它对目录也同样有效。例如,在下面的例子中,我们将所有引用字符串设置成蓝色,并且只要在文档中点击它,就可以跳转到它所引用的对象所在的位置。hyperref
的更多作用我们放置后面讲,在这里,为了使演示效果更好,我们提前使用它。
在引用和被引用的对象在不同页的时候,又分为几种情况。第一种是被引用的对象在当前引用的前面一页,这种情况下则会显示: “索引字符串+on the preceding page”(即,在原本的引用字符串后面增加额外的关于页码的信息,如3.3 on the preceding page),也就是在上一页的意思,也可能显示: “索引字符串+on the facing page”,在对面一页的意思。第二种情况是被引用的对象在当前引用的后面一页,这种情况下则会显示: “索引字符串+on the following page”,在下一页的意思,或者"索引字符串+on the facing page"。实际上,“索引字符串+on the facing page"是以上两种情况中的特殊情况,当我们使用双页模式的时候(book类默认是双页的,见009篇),就会分为左手页和右手页,如果引用和被引用的对象一个在左手页一个在右手页(不管谁在左谁在右),就会出现索引字符串后面有"on the facing page"的情况,意思是在对面的那一页。如果引用和被引用的对象距离超过一页,那么则会显示"索引字符串+on page x”,其中x是页码。而如果引用和被引用的对象在同一页时,就如我们一开始所说,和\ref
一样,只显示"索引字符串"。这里演示一下:
\documentclass{book}
\usepackage[a5paper,margin=1.5in]{geometry}
\usepackage[nospace]{varioref}
\usepackage{kantlipsum} %生成伪文本
% 将引用设置为超链接
\usepackage[colorlinks=true,linkcolor=blue]{hyperref}
\begin{document}
\chapter{A chapter}
\vref{test}
\kant[1]
\vref{test}
\kant[2]
\label{test}
\kant[3]
\vref{test}
\chapter{A chapter two}
\vref{test}
\end{document}
这段代码生成页数较多,而原理并不复杂,自行编译一下即可,不再一一截图。
那么,如果我们指定了选项same-page
,会有什么作用呢?这个选项只对引用和被引用对象在同一页的情况有效。也就是说,在这种情况下,我们在其中输入的文本,就会显示在索引符串的后面。例如,我们将前面的所有\vref
命令都指定选项on the same page
,也就是改为\vref[on the same page]{test}
,那么文档的第二页的引用"1"会变成"1 on the same page"。而其他页的引用均不变。
我们可以看出,\vref
将\ref
和\pageref
整合到了一起,并且内置了一些判断,这正是我们前面致力于实现的。要注意的是,如果我们将页码的格式变成非阿拉伯数字,例如,如果我们在文档中声明了\pagenumbering{roman}
命令(将页码显示为罗马数字),那么"on the preceding/following/facing page"都会转换成on page x
的格式(其中x是一个罗码数字,即页码的roman
格式),只有引用当前页对象的情况不受影响。如果我们在上面的例子中增加一行\pagenumbering{roman}
,那么除了第二页的引用不变,其他页都会变成"索引字符串 + on page x"的格式。
\vref
和\Vref
的区别就像\ref
和\Ref
的区别。可以说,\Vref
就是将\vref
定义中的\ref
换成了\Ref
,以能够将索引字符串中的首字母大写。其他的特性均示\vref
相同。
它们的星号形式,也就是\vref*
和\Vref*
,和原形式的不同之处仅仅是: 星号形式的命令可以将hyperref
的作用无效化。也就是说,如果你使用的是\vref*
或者\Vref*
,那么生成的索引字符串就不再是超链接了,而是普通的文本,既没有颜色也不能跳转。如果你不使用hyperref
包,那么这两种形式的结果都是一样的。