多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE DETAIL

文章详情

深耕前端与后端开发技术的一线实战笔记与踩坑复盘。

LaTeX列表排版实战:itemize与enumerate间距控制及enumitem配置指南

LaTeX列表排版实战:itemize与enumerate间距控制及enumitem配置指南 1. 从一次排版返工说起为什么项目符号值得单独拎出来讲很多人第一次用 LaTeX 写文档时都会经历这样一个阶段正文写得挺顺公式、图表、交叉引用都跑通了结果到了列表这块突然发现出来的东西跟预期完全不一样——要么缩进诡异要么符号和文字对不齐要么嵌套两层之后整个版面就散了。我见过不少同学为了赶进度干脆放弃 LaTeX 的列表环境手动敲-或者•来凑最后编译出来间距忽大忽小打印出来一眼就能看出是手搓的。项目符号itemize和编号列表enumerate看起来是 LaTeX 里最基础的东西但恰恰是这种基础最容易被人跳过。真正把列表用明白涉及到的知识点其实不少itemize和enumerate的默认行为差异、enumitem宏包对间距和符号的精细控制、嵌套列表的符号自动切换规则、列表与段落间距的全局调整、以及列表在 beamer 幻灯片和普通文档中的不同表现。这些细节单独拿出来都不难但凑在一起就构成了一个典型的不踩不知道踩了才知道有多烦的领域。这篇内容适合三类人看第一类是刚接触 LaTeX、被列表缩进搞得一头雾水的新手第二类是已经能用列表、但想让版面更精致的中级用户第三类是需要批量生成结构化文档、想把列表样式统一管理的工程化使用者。我会从最基础的语法讲起逐步深入到enumitem的实战配置、嵌套列表的符号规则、间距微调的计算逻辑以及我在实际项目中踩过的几个坑。全程不堆砌术语每个参数都会解释为什么这么设让你看完就能直接抄作业。2. itemize 与 enumerate 的默认行为先搞清楚 LaTeX 替你做了什么2.1 两个环境的本质区别与默认符号itemize生成的是无序列表默认用实心圆点bullet作为项目符号enumerate生成的是有序列表默认用阿拉伯数字加英文句点。这两个环境的语法结构完全一致都是\begin{...} \item ... \end{...}的形式区别只在于标签的生成方式。\begin{itemize} \item 第一项 \item 第二项 \end{itemize} \begin{enumerate} \item 第一步 \item 第二步 \end{enumerate}看起来很简单但这里有个容易被忽略的点itemize的默认符号并不是固定不变的。当你嵌套第二层时符号会自动从实心圆点变成短横线en-dash第三层变成星号asterisk第四层又回到实心圆点。这个切换规则是 LaTeX 内核写死的定义在\labelitemi、\labelitemii、\labelitemiii、\labelitemiv这四个命令里。同理enumerate嵌套时标签会从1.变成(a)再变成i.再变成A.对应\labelenumi到\labelenumiv。理解这一点很重要因为后面用enumitem改符号时你改的其实就是这些底层命令对应的层级。2.2 默认间距到底是多少一组实测数据LaTeX 的默认列表间距经常被人吐槽太松散。我实测过 article 文档类、10pt 字号下的默认值整理成表格方便对照参数默认值约控制命令列表与上方正文间距约 0.5 倍行距\topsep列表与下方正文间距约 0.5 倍行距\topsep\partopsep项与项之间间距约 0.2 倍行距\itemsep项内段落间距约 0.2 倍行距\parsep左缩进约 2.5em\leftmargin标签与文字间距约 0.5em\labelsep这些值不是随便定的它们来自文档类的\listI、\listii等内部命令。之所以默认偏大是因为 LaTeX 的设计哲学偏向学术排版认为列表是独立于正文的块级元素需要足够的呼吸感。但在实际写技术文档、简历、幻灯片时这种间距往往显得浪费空间。提示不要直接去改\listI这类内部命令它们带符号需要在\makeatletter和\makeatother之间操作容易出错。用enumitem宏包是更稳妥的选择。2.3 一个常见的误解\item后面能不能跟空行新手经常问\item后面能不能空一行再写内容答案是能但行为会变。如果\item后面紧跟空行LaTeX 会认为这一项的内容是一个新段落于是会插入\parsep的额外间距并且首行会有缩进。如果你不想要这个效果就不要在\item和内容之间空行。% 不会产生额外段落间距 \item 这是一项内容 % 会产生段落间距和首行缩进 \item 这是一项内容这个细节在写长列表时特别重要因为一旦某一项不小心多了个空行整个列表的节奏就乱了。3. enumitem 宏包把列表控制权拿回自己手里3.1 为什么是 enumitem 而不是 enumerate 宏包老牌的enumerate宏包也能改标签但它的语法比较别扭而且和itemize不通用。enumitem的优势在于一套接口同时管itemize、enumerate和description支持键值对语法可以全局设置也可以局部覆盖还能直接改嵌套层级的样式。它几乎是现在 LaTeX 列表定制的标准方案。加载方式很简单\usepackage{enumitem}加载之后你就可以在\begin{itemize}后面直接跟方括号参数比如\begin{itemize}[noitemsep]这一行就能把项间距清零。3.2 最常用的几个键noitemsep、nosep、leftmargin我统计过自己项目里用得最频繁的三个键基本能覆盖 80% 的需求noitemsep去掉项与项之间的间距但保留列表与上下文的间距。nosep等价于noitemseptopsep0ptparsep0pt把列表所有垂直间距清零最紧凑。leftmargin控制左缩进可以设成*表示自动计算也可以设具体长度如2em。% 紧凑列表适合简历、幻灯片 \begin{itemize}[nosep, leftmargin1.5em] \item 项目一 \item 项目二 \end{itemize}这里有个经验leftmargin设成*时LaTeX 会根据标签宽度自动调整缩进适合标签长度不一的情况比如description环境。但如果标签是固定宽度的数字或圆点直接给具体值更可控。3.3 全局设置与局部覆盖的优先级enumitem允许你用\setlist做全局配置然后在具体环境里覆盖。优先级规则是局部参数 全局设置 文档类默认。% 全局所有 itemize 都紧凑 \setlist[itemize]{noitemsep, leftmargin1.5em} % 局部这个列表额外加回项间距 \begin{itemize}[itemsep4pt] \item 这一项间距被单独调整 \end{itemize}这个机制在大型文档里非常有用。比如你可以全局把所有列表设成紧凑但在某些需要强调的章节里局部放宽间距。我通常会在导言区写一组全局设置然后在正文里按需微调避免每个列表都重复写参数。3.4 用\setlist统一管理多级嵌套嵌套列表的样式管理是enumitem的强项。你可以按层级分别设置\setlist[itemize,1]{label$\bullet$, leftmargin*} \setlist[itemize,2]{label$\circ$, leftmargin*} \setlist[itemize,3]{label$\diamond$, leftmargin*}这样第一层是实心圆点第二层是空心圆第三层是菱形层次感比默认的圆点-横线-星号更清晰。注意label里用了数学模式$...$因为\bullet、\circ、\diamond都是数学符号。如果你不想进数学模式也可以用文本符号如\textbullet。4. 嵌套列表的符号规则与缩进计算4.1 四层符号的自动切换逻辑前面提到 LaTeX 默认四层符号这里把完整规则列出来方便你决定要不要改层级itemize 默认enumerate 默认对应命令第一层实心圆点1. 2. 3.\labelitemi/\labelenumi第二层短横线(a) (b) (c)\labelitemii/\labelenumii第三层星号i. ii. iii.\labelitemiii/\labelenumiii第四层实心圆点A. B. C.\labelitemiv/\labelenumiv超过四层会报错Too deeply nested。实际上四层已经足够再深就该考虑换一种表达方式了。4.2 缩进是怎么累加的一个具体计算嵌套列表的缩进不是简单叠加而是每层在上一层的基础上增加\leftmargin。假设第一层leftmargin2em第二层默认也是2em那么第二层文字的左边界距离页面左边距就是4em左右还要加上\labelsep等。如果你把每层的leftmargin都设成*LaTeX 会根据标签宽度自动算通常比固定值更紧凑。我在实际项目里的做法是第一层用leftmargin*第二层及以后用leftmargin1.5em这样既保证第一层对齐正文又让嵌套层不会缩进太深。\setlist[itemize,1]{leftmargin*} \setlist[itemize,2]{leftmargin1.5em} \setlist[itemize,3]{leftmargin1.5em}4.3 嵌套时最容易出现的三个问题第一个问题是符号和文字不对齐。这通常是因为label的宽度超过了\labelwidth导致标签溢出到左边距外面。解决办法是用alignleft让标签左对齐或者用labelwidth手动指定宽度。第二个问题是嵌套层之间没有间距。默认情况下父项和子列表之间是有\topsep的但如果你全局设了nosep这个间距也会被清掉导致子列表紧贴父项文字。这时候需要单独给嵌套层加回topsep。第三个问题是子列表的最后一项和父列表的下一项粘在一起。这是因为\itemsep被清零后子列表底部没有额外间距。可以在子列表环境后手动加\vspace或者用before和after键控制。\begin{itemize} \item 父项一 \begin{itemize}[topsep2pt, itemsep1pt] \item 子项一 \item 子项二 \end{itemize} \item 父项二 \end{itemize}5. 间距微调从能用到好看的那几步5.1 垂直间距的四个层次列表的垂直间距分四个层次理解它们才能精准调整\topsep列表整体与上方正文的间距。\partopsep当列表前面有空行时额外增加的间距。\itemsep项与项之间的间距。\parsep项内部段落之间的间距。这四个值共同决定了列表的松紧感。nosep会把它们全部设为零noitemsep只把\itemsep和\parsep设为零。如果你想要列表和正文贴紧但项之间有点呼吸感可以这样\begin{itemize}[topsep0pt, partopsep0pt, itemsep3pt, parsep0pt]5.2 水平间距leftmargin、labelsep、labelindent水平方向有三个关键参数leftmargin整个列表的左缩进。labelsep标签与文字之间的距离。labelindent标签相对于左边距的额外缩进。它们的关系是标签的右边界 leftmargin-labelsep。如果你发现标签离文字太远就减小labelsep如果标签整体太靠左就增大labelindent。\begin{itemize}[leftmargin2em, labelsep0.5em, labelindent0pt]5.3 用\setlist做全局间距方案我在技术文档里常用的一套全局配置是这样的\setlist[itemize]{ leftmargin*, labelsep0.5em, topsep4pt, itemsep2pt, parsep0pt } \setlist[enumerate]{ leftmargin*, labelsep0.5em, topsep4pt, itemsep2pt, parsep0pt }这套配置的效果是列表与正文有轻微间距项与项之间有 2pt 的呼吸感项内段落不额外加间距。实测在 10pt 和 11pt 字号下都表现稳定打印出来不会显得拥挤也不会松散。注意leftmargin*在enumerate里可能会导致标签宽度变化时缩进跳动。如果列表项数超过 9标签从个位数变成两位数建议改用固定值如leftmargin2.2em。6. 特殊场景description、beamer 与自定义标签6.1 description 环境的标签对齐问题description环境默认把标签加粗但标签宽度不固定导致文字起始位置参差不齐。用enumitem可以强制对齐\begin{description}[stylemultiline, leftmargin3cm, alignleft] \item[短标签] 内容 \item[很长的标签] 内容 \end{description}stylemultiline让长标签换行显示alignleft让标签左对齐leftmargin统一控制文字起始位置。这套组合在处理术语表、参数说明时特别有用。6.2 beamer 里的列表间距是另一套逻辑beamer 幻灯片对列表间距的处理和普通文档不同它默认更紧凑而且受\setbeamertemplate影响。如果你在 beamer 里用enumitem的nosep可能会发现效果不明显因为 beamer 自己会覆盖部分间距。在 beamer 里调整列表更推荐用\setbeamertemplate{itemize item}改符号用\addtobeamertemplate改间距。如果非要用enumitem记得加\usepackage{enumitem}后在帧内局部设置不要全局改否则可能影响 beamer 的默认布局。6.3 自定义标签从数字到任意符号enumerate的标签可以用label完全自定义\begin{enumerate}[label\textbf{\arabic*.}] \item 第一项 \item 第二项 \end{enumerate} \begin{enumerate}[label(\alph*)] \item 第一项 \item 第二项 \end{enumerate}\arabic*、\alph*、\roman*分别对应阿拉伯数字、小写字母、小写罗马数字。加*表示自动递增。你甚至可以用\Alph*生成大写字母用\value{enumi}手动控制计数器。7. 踩坑记录那些文档里不会写的细节7.1 列表里的\item后面跟中文标点会出问题这是一个中文用户特有的坑。如果你在\item后面直接跟中文全角标点比如\item内容LaTeX 可能会把标点当成标签的一部分导致缩进异常。正确的做法是\item 内容标点放在内容里。7.2\setlist放在\begin{document}之后会失效\setlist的全局设置必须放在导言区\begin{document}之前。如果放在正文里它只对后面的列表生效而且可能被文档类的默认值覆盖。我见过有人在正文中间写\setlist结果前面的列表没变后面的列表变了排查了半天才发现是位置问题。7.3 列表跨页时的间距异常当一个列表跨页时LaTeX 会在页边界处插入\topsep和\partopsep导致下一页的列表顶部多出一段空白。解决办法是用\raggedbottom或者手动调整\topsep。更彻底的办法是用enumitem的before和after键把间距控制从\topsep转移到环境钩子上。7.4 嵌套列表的计数器重置enumerate嵌套时内层计数器默认不会重置。如果你希望每次外层新项开始时内层从 1 重新计数需要手动重置\begin{enumerate} \item 外层一 \begin{enumerate} \item 内层一 \item 内层二 \end{enumerate} \item 外层二 \setcounter{enumii}{0} % 手动重置 \begin{enumerate} \item 内层一 \end{enumerate} \end{enumerate}这个需求在写步骤说明时很常见但 LaTeX 默认不提供自动重置只能手动处理。8. 一套可直接复用的列表配置模板把前面所有内容整合成一份导言区配置你可以直接复制到自己的文档里\usepackage{enumitem} % 全局列表配置 \setlist[itemize,1]{label$\bullet$, leftmargin*, labelsep0.5em, topsep4pt, itemsep2pt, parsep0pt} \setlist[itemize,2]{label$\circ$, leftmargin1.5em, labelsep0.4em, topsep2pt, itemsep1pt, parsep0pt} \setlist[itemize,3]{label$\diamond$, leftmargin1.5em, labelsep0.4em, topsep2pt, itemsep1pt, parsep0pt} \setlist[enumerate,1]{label\arabic*., leftmargin*, labelsep0.5em, topsep4pt, itemsep2pt, parsep0pt} \setlist[enumerate,2]{label(\alph*), leftmargin1.8em, labelsep0.4em, topsep2pt, itemsep1pt, parsep0pt} % description 环境 \setlist[description]{stylemultiline, leftmargin2.5cm, alignleft, labelsep0.5em}这套配置覆盖了绝大多数场景技术文档、简历、报告、幻灯片。如果你用的是 beamer把topsep和itemsep再调小一点或者干脆用nosep。最后分享一个我自己的习惯每次新建文档时先把这套配置贴进导言区然后写一个包含三层嵌套的测试列表编译看一眼效果。如果没问题后面就不用再管列表样式了。这个先搭框架再写内容的做法帮我省下了大量后期调格式的时间。
返回列表