排版一份带注释的文档时,最先“翻车”的往往不是公式也不是图表,而是脚注。LaTeX 脚注看着只是\footnote{}一个命令,真上手排一篇几十页的稿子,编号错位、脚注跑到下一页、表格里的注号对着错的行、双栏文档里脚注横穿两栏、分隔线长短不合适……这些问题一个都躲不掉。我前后帮某高校实验室改过好几份带大量脚注的报告,也见过 A 同学把整篇论文的脚注从“手动加星号”改回\footnote花了整整一个下午。下面这些内容就是那几次折腾之后沉淀下来的东西:既讲脚注在底层是怎么运作的,也讲表格、浮动体、双栏、尾注这些具体场景该怎么处理,还包括一份我常用的排查顺序。适合刚开始用 LaTeX 写正式文档的人,也适合已经会写但被脚注细节反复折磨的人照着改。
1. 把脚注当“浮动对象”看:机制层面的三个关键事实
很多人对脚注的心理模型是“文字后面跟一段小字”,这个模型在单页短文里成立,一旦文档变长就会失效。脚注在 TeX 里本质上是一个插入项(insertion),它和正文是两条并行的流,最后在页面断行阶段被“拼”到一起。理解这一点,后面百分之八十的怪异现象都能自己推出来。
1.1\footnote背后其实做了四件事
执行一次\footnote{内容},TeX 依次做的事情是:把脚注计数器加一、在当前位置排一个上标记号、把“内容 + 当前编号 + 当前页码”打包成一个插入项塞进\footins这个插入寄存器、并在页面底部预留空间。真正把注文排到页脚,是页面构建阶段(output routine)干的活。这就解释了一个常见现象:正文里写着\footnote,编译出来却出现在下一页的脚注区。原因不是命令写错了,而是当前页面底部剩下的空间不够放下这条注文,插入项被顺延了。
顺延本身不算错误,但在正式出版物里往往是硬伤——读者翻页才能看到注释。想强制它留在本页,最直接的手段是给脚注加footmisc的bottom选项,让 TeX 宁可挤一挤也要把注文放在本页底部。我的经验是:短注文用bottom基本没副作用;注文超过三行时慎用,因为它会挤压正文,偶尔导致本页正文只剩一两行,比注文翻页更难看。
1.2 脚注的“编号”和“注文”是两个可以分开的东西
这条事实是很多高级用法的地基。\footnote是“标号 + 注文”打包版,而\footnotemark只放标号(并递增计数器),\footnotetext只放注文(可指定编号)。之所以要拆开,是因为有些环境不允许在里面写完整脚注,或者写进去之后位置会错:
| 场景 | 直接写\footnote | 推荐做法 |
|---|---|---|
| 普通正文段落 | 正常 | 直接用 |
tabular表格内 | 能编译,注文位置不受控 | \footnotemark+ 表外\footnotetext |
\caption{}内 | 编号错乱、目录里重复出现 | \protect\footnotemark+ 表外\footnotetext |
minipage内 | 注文出现在 minipage 底部 | 视需求,多数情况需要换方案 |
\section{}标题内 | 目录、页眉可能重复触发 | 用可选参数给短标题,脚注放正文 |
beamer的 frame 内 | 需要[frame]选项或改用\footnotetext | 显式指定 |
\footnotemark[n]和\footnotetext[n]带可选参数时,不会递增计数器,而是直接使用你给的数字。这一点必须记牢:如果先写\footnotemark(不带参数)拿到编号 3,再写\footnotetext[3]{},那是对的;但如果两处都不带参数,第二个\footnotetext不会自动配上第一个的编号,而是各自独立。配错编号是表格脚注最常见的 bug 来源。
1.3 计数器是全局的,minipage里那份是“私有的”
footnote是一个 LaTeX 计数器。minipage、\parbox这类盒子环境会复制一份计数器在内部使用,所以 minipage 里的脚注从 1 开始重新编号,注文也排在 minipage 自己的底部而不是页面底部。这个设计本身合理(图注、边栏里的小表格确实该有自己的编号体系),但它经常被误认为是 bug——有人在minipage里放了三个脚注,发现页面底部空空如也,注文全挤在那张小图的下面,第一反应是“脚注坏了”。
如果你需要 minipage 内的脚注和全局共用编号,就得在 minipage 内部把计数器手工同步回去,或者干脆不用 minipage、改用\parbox之外的排版手段。反过来,如果你想要独立编号,那 minipage 的默认行为正好省事。判断标准很简单:这段脚注是“这段小内容自己的注”,还是“全文注释体系的一部分”。前者留着默认行为,后者就得拆开处理。
2. 表格与浮动体里的脚注:三种方案的实际取舍
表格脚注是我被问得最多的一类问题。核心矛盾在于:表格内容经常被浮动到别的位置(顶部、下一页),而脚注是跟着“当前位置”走的,两者一错位,注号就对不上了。所以不要指望一个通用解法,得按表格的复杂程度分三档处理。
2.1 简单表格:\footnotemark与\footnotetext手工配对
最轻量的做法,不引入任何宏包。要点是:表格内只放\footnotemark,所有注文统一写在\end{tabular}之后、\end{table}之前。这样注文的位置由你控制,不会跟着单元格跑。
\begin{table}[htbp] \centering \begin{tabular}{lcc} \toprule 方案 & 耗时 & 峰值内存\footnotemark \\ \midrule 基准实现 & 1.24 & 31 \\ 缓存改造 & 0.83 & 47\footnotemark \\ \bottomrule \end{tabular} \footnotetext[1]{耗时单位秒,同一台机器三次运行取平均。} \footnotetext[2]{内存单位 MB,含运行时分配开销。} \caption{两种实现的资源占用对比} \end{table}这段代码里有两个细节值得单独说。第一,第一个\footnotemark不带参数,会消耗掉当前计数器值(假设是 1);第二个同理拿到 2。后面的\footnotetext[1]和[2]是显式指定,不会再次递增,所以最终编号是连续的。第二,如果表格浮动到了下一页,注文会跟着表格一起走,因为它们在同一个浮动体内部——这正是我们要的效果。反过来说,如果注文写在表格外面,浮动一移,注号就和注文分家了。
这种做法的代价是手工维护编号。表格里加一行、删一行,后面的编号全要跟着改。所以我的原则是:表格脚注超过两条,就换下一档方案。
2.2 中等复杂表格:threeparttable把表注钉在表格下方
threeparttable的思路很朴素:给表格加一个“表注区”,表注用\tnote{a}之类的小标记,而不是走全局脚注计数器。好处是彻底摆脱编号依赖,表注跟着表格走,永远不会串号;坏处是标记体系独立于正文脚注,读者看到的表格里是 a、b、c,正文里是 1、2、3,得在心里做一次换算。
\usepackage{threeparttable} % ... \begin{table}[htbp] \centering \begin{threeparttable} \begin{tabular}{lcc} \toprule 方案 & 耗时\tnote{a} & 峰值内存\tnote{b} \\ \midrule 基准实现 & 1.24 & 31 \\ 缓存改造 & 0.83 & 47 \\ \bottomrule \end{tabular} \begin{tablenotes}[flushleft] \footnotesize \item[a] 耗时单位秒,同机三次平均。 \item[b] 内存单位 MB,含运行时开销。 \end{tablenotes} \end{threeparttable} \caption{两种实现的资源占用对比} \end{table}tablenotes环境支持[flushleft]、[para]等选项,前者让表注左对齐(我个人偏好,看起来更像正式出版物的表注),后者把多条表注排成一段。\tnote的标记默认是小写字母,可以在导言区用\renewcommand{\tnote}[1]{...}改成上标字母或其他样式。还有一个容易被忽略的点:threeparttable的宽度取自内部tabular的宽度,所以表注会自动和表格同宽折行,不需要手动设\parbox。
如果表格还需要“表格宽度等于文本宽度”这种效果,可以直接用threeparttablex包,它提供ThreePartTable环境配合tabularx,我这里不展开,但值得知道它的存在。
2.3\caption里塞脚注:编号重复的真正原因
有些人习惯在图注里加“数据来源”这类说明,第一反应是\caption{...\footnote{数据来自某次公开测试。}}。这么写通常会出问题:编号跳号,或者同一段注文出现两次。原因是\caption的参数会被写进.lof/.lot文件(用于生成图表目录),写文件的过程中脚注又被执行了一次,计数器就多走了一格。
正确的写法是两步:\caption里用\protect\footnotemark,注文用\footnotetext放在\caption之后。\protect的作用是防止命令在写文件时被展开。
\begin{figure}[htbp] \centering \includegraphics[width=0.8\linewidth]{curve.pdf} \caption{不同批量下的吞吐变化\protect\footnotemark} \footnotetext{测试数据来自压测脚本的单轮结果。} \label{fig:throughput} \end{figure}这里\footnotemark不带参数,占掉当前编号;紧接着\footnotetext不带参数——注意,\footnotetext不带参数时用的是当前计数器值,而此时计数器已经被\footnotemark递增过了,所以两者编号会差一!稳妥做法是给\footnotetext显式写出编号,或者用\addtocounter{footnote}{-1}回调一格。这一点在很多教程里被含糊带过,实际是表格/图注脚注编号不对的头号原因,我建议你养成“配对必写显式编号”的习惯,宁可多敲两个字符。
提示:涉及
\caption、\section、\item等会“搬运”参数的命令时,脚注相关命令一律加\protect,这是最省心的通用做法。
另外提一句\section{}里写脚注的问题。\section{标题\footnote{说明}}能编译,但标题会同时进目录和页眉,脚注可能被触发两次。正规做法是用可选参数给一个不带脚注的短标题:\section[短标题]{长标题\footnote{说明}},目录里只出现短标题,脚注只排一次。
3. 让脚注长得对:footmisc的常用开关与参数换算
内容排对了之后,下一个问题是“好不好看”。LaTeX 默认脚注风格带首行缩进、分隔线固定长度、字号是\footnotesize,这套风格偏学术期刊,不一定合你的文档调性。footmisc是目前最省事的调整入口,但它的选项组合有讲究,乱配会互相打架。
3.1bottom、hang、flushmargin三个开关的组合效果
这三个选项名字看着接近,作用完全不同,我列个对照:
| 选项 | 作用 | 什么时候用 | 副作用 |
|---|---|---|---|
bottom | 强制注文留在本页底部 | 短注文、正式出版物 | 可能挤压正文,页尾留白变大 |
hang | 注文悬挂缩进,第二行与第一行文字对齐 | 注文较长、需对齐阅读 | 首行与标号之间留白偏大 |
flushmargin | 取消首行缩进,注文顶格排 | 窄栏、双栏文档 | 与默认风格差异较大 |
multiple | 多个标号共用一个注文 | 同一处引用多个来源 | 注文内分隔符需自定义 |
para | 同页所有注文连排成一段 | 注文极多且都很短 | 长注文会连成一大块 |
symbol | 用* † ‡等符号代替数字 | 图表注、致谢 | 超过 9 个会循环 |
实际使用中我最常用的是[bottom,hang]这组:hang让长注文折行后整齐对齐,视觉上比默认的“每行都缩进”舒服很多。如果是双栏文档,加flushmargin更好看,因为窄栏里首行缩进会吃掉本就不多的行宽。
\usepackage[bottom,hang]{footmisc}要注意加载顺序。footmisc和hyperref有已知的交互问题(脚注链接的锚点位置可能错),实测下来比较稳的做法是让hyperref先加载、footmisc后加载;如果仍然出现链接跳错位置,给footmisc加stable选项能缓解大部分情况。这两个包谁先谁后在不同 TeX 发行版上表现略有差异,我的建议是改完之后点一下 PDF 里的注号,看跳转是否正确,别只看编译有没有报错。
3.2 分隔线、间距、字号的手工微调
默认分隔线是一段约 0.4pt 粗、占栏宽 40% 的横线。这个长度在 A4 单栏里偏短,在双栏里又偏长,基本都得改。改法是重定义\footnoterule:
\renewcommand{\footnoterule}{% \kern-4pt \hrule width 0.3\columnwidth height 0.4pt \kern 3pt }\kern的负值是在“往上提”,正值为“往下压”,目的是控制横线上下与正文、注文之间的呼吸感。这两个数值不要凭感觉乱给,我的经验是:横线上方留 4pt、下方留 3pt 在 10pt 正文字号下比较接近常见出版物的视觉比例;如果你用的是 12pt 正文,把数值按 1.2 倍放大即可。
注文与正文之间的整体距离由\skip\footins控制,footmisc的bottom模式下这个值会被重新计算。想手动干预:
\setlength{\skip\footins}{1.2\baselineskip plus 4pt minus 2pt}后面的plus/minus是弹性量,TeX 在页面紧张时会自动伸缩。给一点弹性比写死一个固定值更安全,写死容易在跨页处产生难看的空隙。至于注文字号,默认是\footnotesize,一般不用动;真有特殊需求(比如某些期刊要求 8pt 固定),用\renewcommand{\footnotesize}{\fontsize{8}{9.6}\selectfont}覆盖,但要注意这会连带影响图表标题等所有使用\footnotesize的地方,改之前先全文搜一遍。
3.3 符号标记与符号循环:星号、剑号、双剑号
致谢、图注、作者单位这类脚注,用数字反而显得生硬,符号更合适:
\renewcommand{\thefootnote}{\fnsymbol{footnote}}\fnsymbol提供的序列是* † ‡ § ¶ ‖等,只到 9 个。超过 9 个会报Counter too large错误。所以用它之前先数一下同页最多几条脚注。如果确实会超过,两个办法:一是用footmisc的symbol*选项,它会在符号用尽后自动切换到数字;二是手工定义一套循环标记:
\renewcommand{\thefootnote}{% \ifcase\value{footnote}\or *\or\textdagger\or\textdaggerdbl\fi}不过手工定义这套写法在计数器超过 3 之后会输出空内容,所以更稳的是symbol*。
还有一个常见需求:同一页里既有符号脚注(作者单位),又有数字脚注(正文注释)。这时候单一计数器不够用,得启用多套编号体系,那就是第 6 节要讲的manyfoot/bigfoot的活儿了。简单说,manyfoot定义多条平行的脚注流,各自有独立的计数器、标记样式和分隔线,代价是配置稍繁琐、与某些包的兼容性要逐个验证。
4. 编号策略:全局连续、每章重置、每页重置
编号规则看着是小事,但在长篇文档里直接影响可读性。一本几十页的报告,脚注编号一路从 1 编到 200,读者看到一个“137”根本无从定位;反过来每页重置,同一页里出现两个“1”的情况又容易混淆。所以选哪种策略,取决于文档篇幅和阅读方式。
4.1 全局连续:默认行为,别轻易改
LaTeX 默认就是全文连续编号,这也是最省心的方案。短文档(20 页以内)、脚注总数不多的场景,保持默认即可,不需要任何配置。
需要留意的是:全局连续编号下,\footnotemark/\footnotetext的手工配对更容易出错,因为编号依赖前面所有脚注的累计数。所以用这类手工配对时,尽量不要在文档中途增删脚注,或者干脆改用threeparttable那种独立标记体系。
4.2 每节或每章重置:\counterwithin*的正确用法
学术报告和书籍常要求脚注按章重置。标准做法是用chngcntr宏包的星号版本:
\usepackage{chngcntr} \counterwithin*{footnote}{section}星号不能省。不带星号的\counterwithin会把\thefootnote重定义成“节号.注号”的形式,也就是输出“2.1”这种编号,多数场景下并不想要这个效果。带星号只做计数器归零,\thefootnote保持原样,输出仍然是单纯的 1、2、3。
如果文档用\chapter分章,把第二个参数换成chapter即可。不想引入chngcntr的话,手写版本是:
\makeatletter \@addtoreset{footnote}{section} \makeatother效果一样,但chngcntr的好处是会在\section*等场景下表现更规范,我一般直接用包。
这里有个坑值得提前说:重置之后,表格里的\footnotetext[1]显式编号就跟新章节的编号体系对不上了。所以一旦决定了按节重置,表格脚注就不要再手写数字,改用threeparttable,或者用相对编号 +\addtocounter计算。这是编号策略和表格方案之间必须一起考虑的地方,分开决定往往要在后期返工。
4.3 每页重置:perpage与footmisc的perpage选项
有些出版物(尤其是图册、手册)要求脚注每页从 1 开始。做法有两种,效果基本一致:
\usepackage{perpage} \MakePerPage{footnote}或者用footmisc自带的选项:
\usepackage[perpage]{footmisc}两者都需要至少编译两次才能让编号稳定,因为页码信息是在前一次运行中记录的。第一次编译出来编号可能是错的,别慌,再跑一遍。另外,每页重置之后,跨页的长注文会继承前页编号,这一点没有完美的解决方案,只能尽量把长注文拆短。
还有一个变体是footmisc的perpage配合symbol*,实现“每页符号编号、用尽后转数字”。这个组合在需要在一页内区分多个来源(比如多个数据来源标注)时特别好用,我在几份数据报告里用过,读者反馈比纯数字清晰。
4.4 手动编号:什么时候值得冒这个险
\footnotemark[42]、\footnotetext[42]这种完全手动的写法,唯一合理的场景是“注释由外部系统生成、编号必须与外部一致”。比如某些投稿系统要求脚注编号对应参考文献表里的条目号。除此之外我不建议手动编号,理由很实在:文档一旦增删内容,手工编号全废,而且要逐个核对,工作量比重写还大。
真要用的话,记住两条:显式编号的\footnotemark[n]不会递增计数器,所以后面不带参数的脚注会接着用“没被消耗的那个值”,容易出现重号;解决办法是在必要处用\setcounter{footnote}{n}强制同步计数器。这条规则听起来绕,实际调试时在可疑位置插一句\typeout{FOOTNOTE=\the\value{footnote}}打印当前值,比猜快得多。
5. 那些容易踩的坑:一份可复现的排查链路
脚注出问题时的麻烦在于“症状离原因很远”。下面这几条是我实际遇到过的,按“症状 → 排查动作 → 根因 → 修法”的顺序写,你可以照着顺序走一遍,不用每次从零开始推理。
5.1 脚注“消失”了:先查盒子环境,再查beamer
症状:正文里明明写了\footnote,PDF 里只有上标,页面底部找不到注文。
排查第一步:往上下各翻两页,看注文是不是被顺延到了下一页底部。这是最常见的情况,尤其当页面底部只剩一两行空间时。如果是顺延,加bottom选项或者调整该段落位置。
排查第二步:看这段内容是不是在minipage、\parbox、\fbox里。盒子环境内的脚注排在盒子底部,盒子小的时候可能被裁掉或者挤在极窄的空间里,视觉上像“消失了”。解决办法是把盒子内容提出来单独处理,或者接受盒子内的独立编号体系。
排查第三步:如果是beamer,frame里的\footnote需要显式告诉它按 frame 还是按整篇编号,写法是\footnote[frame]{...};不加选项时有时会整体不显示。这个坑在演示文稿里非常常见,第一次遇到基本都要查半天。
5.2 长 URL 撑破脚注行宽
脚注里放网址,如果网址里没有可断行的位置(比如一长串没有连字符的路径),就会直接伸出右边距。\url命令解决了等宽字体问题但没解决断行问题。
\usepackage{xurl} % 允许在任意字符处断行,比 url 包激进xurl是url的加强版,允许在斜杠、点号之外的位置断行,对长路径效果明显。代价是断行位置可能不太美观,会出现在字符串中间。如果不希望断得太随意,退回url包并配合\Urlmuskip微调断行惩罚值:
\usepackage{url} \setlength{\Urlmuskip}{0mu plus 1mu}另外记住一条:脚注里不要用\verb。\verb在会被“搬运”的参数里(脚注、标题、表格单元格)会直接报错,因为它依赖特殊的字符处理机制。需要等宽内容就用\texttt{}配合\detokenize,或者用\url。
5.3 双栏文档里脚注横穿两栏
双栏排版时,默认脚注会横跨整个页宽排在页面最底部,而不是只占当前栏的宽度。学术论文里这通常是对的,但在一些杂志风格的排版里会显得很怪。
想要“脚注只在当前栏宽度内”,加载dblfnote宏包:
\usepackage{dblfnote}它会重定义双栏模式下的脚注行为,让注文只占当前栏宽。代价是每栏能容纳的注文变少,长注文更容易顺延到下一页。另一个办法是用footmisc的marginal选项把脚注改成边注,彻底绕开这个问题,但边注要求页面外侧有足够的页边距,A4 默认版式下通常不够,得配合geometry一起调。
顺带说一个双栏特有的现象:如果左栏底部有脚注、右栏正文很长,两栏的底部基线会不齐。这不是 bug,是插入项占位导致的。\raggedbottom能让页面底部不强制对齐,视觉上舒服一些。
5.4hyperref与脚注跳转错位
开了hyperref之后,PDF 里点击注号应该跳到注文。如果跳到了别的位置,或者注文上有反向链接但指向错的页,通常是包的加载顺序问题。
我用的固定顺序是:hyperref尽量靠后加载,footmisc放在hyperref之后。如果仍然跳错,检查是不是同时用了footmisc的bottom和hyperref——这两者在“注文实际位置与记录位置不一致”时会互相干扰,stable选项就是为这类情况准备的:
\usepackage[bottom,stable]{footmisc}还有一个小细节:hyperref会给脚注生成锚点,如果同一页有\footnotemark/\footnotetext手工配对,锚点可能只落在 mark 上、text 上没有反链。这属于可接受的降级,不用强求。
6. 尾注与多套编号:enotez、manyfoot什么时候值得上
内容多了之后,脚注会变成“翻到页底还得回来”的负担。有些文档类型(技术报告、数据集说明)更适合把所有注释集中到文末,也就是尾注。另外还有一类需求是“同一页里并存两套编号”,比如正文注释用数字、图片来源用符号,这就得多套脚注流。
6.1 用enotez把脚注集中到文末
enotez是这几年比较推荐的尾注方案,语法接近\footnote,迁移成本低:
\usepackage{enotez} \setenotez{backref = true, list-name = 注释} % 正文里照常写 正文内容\footnote{这条注释会被收集到文末。} % 文末输出 \printendnotesbackref=true会在尾注列表里生成“返回正文”的小链接,这对长文档很关键——否则读者看到第 40 条注释,根本不知道它在正文哪儿。list-name改标题,中文文档里设成“注释”或“注”比默认的英文标题自然。
这里有个容易忽略的点:enotez默认接管\footnote的含义(把它变成尾注收集),所以不能和原来的脚注混用。如果需要“一部分是脚注、一部分是尾注”,得用\DeclareNoteType定义新的注释类型,或者干脆老实用manyfoot分两套流。
比起老牌的endnotes包,enotez在hyperref兼容性和格式定制上更现代一些。endnotes的优势是历史长、各种模板里都能见到,如果接手的是别人写好的模板,别急着换,先看模板里已经用了什么。
6.2 多套编号体系:manyfoot的配置思路
真实场景是这样的:一篇报告里,正文注释要用 1、2、3 连续编号,图片和数据来源用*、†符号,致谢部分又是另一套。单一计数器做不了,manyfoot就是干这个的:
\usepackage{manyfoot} \DeclareNewFootnote[plain]{src} % 第二套:来源注 \renewcommand{\thefootnoteA}{\fnsymbol{footnoteA}} % 给它换成符号\DeclareNewFootnote会生成一组新命令,比如声明了src,就得到\footnotemarksrc和\footnotetextsrc。使用时两套互不干扰,各自有独立计数器。配置项里的plain是样式名,也可以自定义成roman(罗马数字)等。
这玩意儿功能强,代价是配置麻烦、容易和其他包冲突。我的判断标准是:只有当“两套编号必须在同一页共存”时才值得上manyfoot;如果只是想让不同章节用不同样式,用\renewcommand{\thefootnote}{...}在章节开头改一下就行,不需要引入新包。bigfoot是另一个同类选择,它在多套脚注的排版细节(比如多套分隔线、注文宽度)上更灵活,但学习曲线更陡,我一般不到万不得已不用。
还有一类替代思路值得提一下:如果所谓“第二套编号”其实只是图表来源标注,threeparttable的\tnote或者直接在图表下方写一行\footnotesize说明文字,可能比引入manyfoot简单得多。工具选择上,我始终倾向于“能用轻方案解决就别上重方案”,因为模板是要给别人用的,越少的宏包意味着越少的兼容性惊喜。
最后分享一个我在实际操作中的体会:脚注相关的问题,九成能在“是不是盒子环境”“是不是被搬运的参数”“是不是编号配错”这三个方向上找到答案。每次改完脚注配置,别只看编译有没有报错,一定要把 PDF 翻到有脚注的那几页,逐条对一遍注号和注文,再点一下超链接看跳转——这三步加起来不到两分钟,但能省掉后期校对时几小时的返工。