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}
宏包文件不写 \documentclass 或 document 环境,也不放待排版的普通正文。它只提供定义和设置。
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 中的计数器 #
常见名称有 section、subsection、equation、figure、table、page、footnote,以及列表层次的 enumi、enumii 等。
secnumdepth 控制章节编号深度,tocdepth 控制目录显示深度:
% 导言区
\setcounter{secnumdepth}{3}
\setcounter{tocdepth}{2}
在文章类中,section、subsection、subsubsection 的层级分别是 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 接口。已有 caption、geometry 等专用宏包时,优先用它们提供的设置命令。