8. 自定义 LaTeX 命令和功能

8. 自定义 LaTeX 命令和功能 #

如果相同写法反复出现,可以给它一个有意义的名字。例如把“实验结果的格式”集中定义为命令,以后就能只改一处,而不是逐个替换正文。

8.1 自定义命令和环境 #

8.1.1 定义新命令 #

\newcommand 定义命令,参数个数写在可选参数中,定义里的 #1#2 等对应输入参数:

% 导言区
\newcommand{\projectname}{测量实验}
\newcommand{\term}[1]{\textbf{#1}}
\newcommand{\result}[2]{#1:\emph{#2}}

% 正文
\projectname{}的\term{主要结果}如下。

\result{结论}{两组数据存在差异。}

最多有 9 个参数。无参数命令后接英文单词时,用 {} 避免空格被忽略。

命令 定义不存在时 定义已存在时
\newcommand 创建。 报错,避免意外覆盖。
\renewcommand 报错。 修改。
\providecommand 创建。 保持原定义。

不要为绕过冲突而随意改用 \renewcommand。先确认同名命令属于哪个宏包,以及覆盖是否符合预期。

传统命令也可以设置第一个可选参数的默认值:

\newcommand{\notice}[2][提示]{\textbf{#1:}#2}

这里总共两个参数,第一个可选,第二个必选。正文中 \notice{请保存源码。} 使用“提示”,\notice[注意]{先检查第一条错误。} 使用“注意”。

8.1.2 定义环境 #

\newenvironment{名称}[参数个数]{开始代码}{结束代码} 包裹环境内容:

% 导言区
\newenvironment{important}[1][重要]{%
  \begin{quote}
  \textbf{#1:}\ignorespaces
}{%
  \end{quote}
}

% 正文
\begin{important}[编译前]
检查文件名、宏包依赖和编译器设置。
\end{important}

命令名带反斜线,环境名本身不带反斜线。开始代码和结束代码中的环境也必须配对;\renewenvironment 用于修改已有定义。

\ignorespaces 忽略环境起始处不需要的源码空白,行尾 % 则避免定义中的换行产生意外空格。

8.1.3 xparse 宏包简介 #

xparse 提供更灵活的命令和环境参数接口。LaTeX 2020-10-01 起,主要接口已集成到内核中,现代环境通常不必显式加载 xparse

\NewDocumentCommand{\命令名}{参数规格}{定义}
\NewDocumentEnvironment{环境名}{参数规格}{开始代码}{结束代码}
规格 含义
m 必选参数。
o 方括号可选参数,缺省时返回特殊的未提供标记。
O{默认值} 带默认值的方括号可选参数。
s 可选星号,返回布尔值。
+m 允许参数中包含段落。

检查可选参数是否提供,用 \IfNoValueTF,不要把未提供标记当普通字符串比较:

% 导言区
\NewDocumentCommand{\hello}{om}{%
  \IfNoValueTF{#1}
    {你好,#2!}
    {你好,#1 和 #2!}%
}

% 正文
\hello{小林}
\hello[小周]{小林}

检查星号用 \IfBooleanTF

% 导言区
\NewDocumentCommand{\keyword}{sm}{%
  \IfBooleanTF{#1}{\textbf{#2}}{\emph{#2}}%
}

% 正文
\keyword{普通强调}
\keyword*{加粗强调}

\IfNoValueT\IfNoValueF 和对应的布尔测试只执行其中一个分支。\RenewDocumentCommand\ProvideDocumentCommand\DeclareDocumentCommand 分别表示修改已有、仅在不存在时定义、无条件定义;环境有对应的命令族。

环境名中的星号与 s 参数不同。名为 important* 的环境是另一个环境;s 参数则写在 \begin{环境名} 后面,例如 \begin{envstar}*

8.2 编写自己的宏包和文档类 #

8.2.1 编写简单的宏包 #

把可复用定义保存为 projectstyle.sty,文件名与 \ProvidesPackage 的名称一致:

% 文件:projectstyle.sty
\NeedsTeXFormat{LaTeX2e}
\ProvidesPackage{projectstyle}[2026/09/19 Project commands]
\RequirePackage{xcolor}
\definecolor{projectblue}{RGB}{0,90,150}
\newcommand{\projecttitle}{测量实验}
\newcommand{\projectnote}[1]{\textcolor{projectblue}{#1}}

同一目录下的 main.tex 可以直接调用:

\documentclass[UTF8,fontset=fandol]{ctexart}
\usepackage{projectstyle}

\begin{document}
\section{\projecttitle}
\projectnote{数据已经过初步检查。}
\end{document}

宏包文件不写 \documentclassdocument 环境,也不放待排版的普通正文。它只提供定义和设置。

8.2.2 在宏包中调用其它宏包 #

宏包内用 \RequirePackage 声明依赖,语法类似 \usepackage。上例在定义颜色前加载 xcolor,使用者无需猜测依赖。

同一宏包可能被多个入口加载,选项仍要一致。不要把所有“可能用到”的宏包都塞进公共配置,否则更容易引发冲突。

8.2.3 编写自己的文档类 #

文档类使用 .cls 扩展名。简单模板可以基于已有类,而不从头定义所有章节和页面命令。

把下面内容保存为 studynote.cls

% 文件:studynote.cls
\NeedsTeXFormat{LaTeX2e}
\ProvidesClass{studynote}[2026/09/19 Study notes]
\LoadClass[UTF8,fontset=fandol,a4paper]{ctexart}
\RequirePackage[margin=2.5cm]{geometry}

然后用以下主文件:

\documentclass{studynote}
\begin{document}
\section{学习笔记}
这份文档使用自定义文档类。
\end{document}

\ProvidesClass 声明类名,\LoadClass 加载基础类。这里只演示最小封装;向基础类传递用户选项、设计完整论文模板等,需要进一步学习类文件接口,不能假定这个小例子支持任意选项。

8.3 计数器 #

8.3.1 定义和修改计数器 #

章节、公式、图表和列表编号都由计数器管理。计数器名称不带反斜线:

% 导言区
\newcounter{exercise}[section]
\renewcommand{\theexercise}{\thesection.\arabic{exercise}}
\newcommand{\exercise}{%
  \par\refstepcounter{exercise}%
  \noindent\textbf{练习 \theexercise}\quad
}

% 正文
\section{练习}
\exercise\label{ex:first} 写出一个带编号的公式。

参见练习~\ref{ex:first}。

[section] 表示节计数增加时,练习计数归零。本例补充使用 \refstepcounter,让紧随其后的 \label 能记录这个编号。

命令 作用
\setcounter{exercise}{3} 直接设为 3。
\addtocounter{exercise}{2} 加 2。
\stepcounter{exercise} 加 1,并重置下级计数器。
\refstepcounter{exercise} 递增并建立可引用的当前编号。

8.3.2 计数器的输出格式 #

\theexercise 决定显示格式,不等于计数器的内部数值。可选格式包括:

命令 显示
\arabic{exercise} 阿拉伯数字。
\alph{exercise}\Alph{exercise} 小写、大写字母;正常字母编号范围为 1–26。
\roman{exercise}\Roman{exercise} 小写、大写罗马数字。
\fnsymbol{exercise} 一组脚注符号,编号范围有限。

命令参数必须是计数器名,不能写成 \roman{3}。超出字母或脚注符号的可表示范围会出错;仅重定义 \the... 也不会改变上下级的重置关系。

8.3.3 LaTeX 中的计数器 #

常见名称有 sectionsubsectionequationfiguretablepagefootnote,以及列表层次的 enumienumii 等。

secnumdepth 控制章节编号深度tocdepth 控制目录显示深度

% 导言区
\setcounter{secnumdepth}{3}
\setcounter{tocdepth}{2}

在文章类中,sectionsubsectionsubsubsection 的层级分别是 1、2、3。上例让三级标题编号,但目录仅显示到二级。没有编号不等于不进入目录;星号标题则通常两者都不自动生成。

8.4 LaTeX 可定制的一些命令和参数 #

标题文字用 \renewcommand,长度用 \setlength,不要混用:

% 导言区
\renewcommand{\contentsname}{内容目录}
\setlength{\tabcolsep}{8pt}
类型 常见命令或参数
目录名称 \contentsname\listfigurename\listtablename
图表前缀 \figurename\tablename
文献标题 文章类的 \refname,书籍类的 \bibname
其他标题 \abstractname\indexname
盒子 \fboxrule\fboxsep
表格 \tabcolsep\arraycolsep\arrayrulewidth
图表标题间距 \abovecaptionskip\belowcaptionskip
分栏 \columnsep\columnseprule

\tabcolsep 是单元格一侧的留白,相邻两列之间通常有两份。@{} 列格式会改变默认留白。

ctex 已处理中文章标题等格式;仅修改 \chaptername 并不足以完整构造“第 X 章”。中文标题样式应查 ctex\ctexset 接口。已有 captiongeometry 等专用宏包时,优先用它们提供的设置命令。

← 绘图功能 · 下一篇:排除错误、寻求帮助 →

来源与许可