第一份 LaTeX 文档可以只有一行,而且真的能编译:\documentclass{article}\begin{document}Hi\end{document}。把它交给 pdflatex,确实会得到一个一页、11,529 字节的 PDF。多数人在第一份文档上卡住,是因为跳过了这一行到底在做什么,转而抄了一大段模板。本页就从这个最小形出发向外扩展:序言区与正文的分界在哪里、怎么运行编译器、凭空冒出来的 .aux 和 .log 究竟是什么、为什么有时要编译两次,以及每个新手第一天必踩的 % 和 $ 陷阱——每一步都附上真实的终端输出。
能编译通过的最小 LaTeX 文档(hello world)
需要的命令只有三条。\documentclass{article} 声明这是哪一类文档,\begin{document} 打开正文,\end{document} 收尾。三者缺一,就得不到 PDF;三者齐备,即使中间什么都不写也照样能跑。先把下面这个带换行的可读版本存成 hello.tex。文件编码用 UTF-8,扩展名必须是 .tex。
\documentclass{article}
\begin{document}
This is my first document.
\end{document}\documentclass{article} 里的 article 就是文档类。类是整份文档的设计图:页边距多宽、标题多大、前后留多少空、有没有「章」这一层,都由它一并决定。随发行版自带的有 article(论文、短报告、技术笔记,没有 \chapter)、report(带章的较长报告)、book(按双面印刷设计的书籍)和 letter(书信)。写中文可以配合 ctex 系列,写日文则选 jlreq 或 jsarticle。如果期刊或出版社提供了自己的类文件,别犹豫,直接用:关于体例的所有争论会一次性消失。
序言区与正文:\begin{document} 分开的两个世界
\begin{document} 之前是序言区,之后是正文。序言区是决定「接下来怎么排」的地方,写在那里的东西不会印到纸上;正文则相反,写什么基本就出什么。这条界线不是 LaTeX 的癖好,而是必然:在排出第一个字符之前,LaTeX 必须先把纸张尺寸、行宽、所用字体、要加载的宏包统统定下来。所以 \usepackage 只能写在序言区。写进正文,编译就会停在 ! LaTeX Error: Can be used only in preamble.
反过来的错误同样常见:在序言区写了普通句子,就会得到 ! LaTeX Error: Missing \begin{document}.。第一次见到会很困惑——\begin{document} 明明写了啊——但 LaTeX 的意思是「正文还没开始,字就来了」,也就是在正文起点之前出现了文字。留在序言区的备注请用 % 注释掉。
编译成 PDF:pdflatex 与 latexmk
在终端里敲 pdflatex hello.tex 就行。如果你用的是编辑器或 Overleaf,那个「编译」按钮做的正是这件事。成功的标志是最后两行——Output written on hello.pdf 和 Transcript written on hello.log。看到这两行,PDF 就已经生成了。中间刷过去的一大堆路径,是被加载的类文件和字体清单,属于正常输出,不必去读。
$ pdflatex hello.tex
This is pdfTeX, Version 3.141592653-2.6-1.40.26 (TeX Live 2024)
(./hello.tex
LaTeX2e <2023-11-01> patch level 1
(/usr/local/texlive/2024/texmf-dist/tex/latex/base/article.cls
Document Class: article 2023/05/17 v1.4n Standard LaTeX document class
...
Output written on hello.pdf (1 page, 31014 bytes).
Transcript written on hello.log.出错停下时,pdflatex 可能会打出一个 ? 等你输入。别慌,也别猛敲回车——输入 x 再回车就能中止。如果不想每次都陪它对话,用 pdflatex -interaction=nonstopmode hello.tex,有错也不停,一口气跑完并把一切写进日志。另外,早点记住 latexmk -pdf hello.tex 会省事:它会按文档实际需要自动重跑相应次数,下一节的「两次」问题就不再是你的问题了。
多出来的文件都是什么:.aux、.log、.toc、.out
只编译一次,文件夹里就多出四个文件。这不是出了毛病,而是 LaTeX 在给自己留便条。把一份带目录和一个节的文档交给 pdflatex,除了 hello.tex 之外,你会得到 hello.aux(132 字节)、hello.log(3,250 字节)、hello.pdf(31,014 字节)和 hello.toc(59 字节)。其中 .aux 是整套机制的心脏。打开来看,里面只有三行。
% hello.aux, written by the first run
\relax
\@writefile{toc}{\contentsline {section}{\numberline {1}Introduction}{1}{}\protected@file@percent }
\gdef \@abspage@last{1}读出来就是:「第 1 节 Introduction 在第 1 页」「最后一页是第 1 页」。也就是说,.aux 是一本记录编号与页码位置的笔记本,会在下一次运行时被读回。.toc 是据此生成的目录草稿,.log 是整个处理过程的完整记录,.out 则是在用了 hyperref 之后才出现的,存放 PDF 的书签信息。
| 扩展名 | 里面是什么 | 能不能删 |
|---|---|---|
.tex | 你写的稿件,唯一的原本 | 绝对不能删。要备份、要进 Git 的只有它 |
.pdf | 最终输出 | 可以删,随时能从源文件重新生成 |
.aux | 节号、图表号,以及每个标签指向的页码 | 可以删,但紧接着的那一次运行,引用会显示成 ?? |
.log | 加载过的每个文件、每条警告、每个错误 | 可以删。但在排查错误时,它是你手头最有价值的东西 |
.toc | 上一次运行写下的目录条目与页码 | 可以删;下一次运行只是目录变空而已 |
.out | hyperref 生成的 PDF 书签 | 可以删;不加载 hyperref 时根本不会出现 |
实务上的结论很短:只把 .tex 和图片纳入版本控制,辅助文件写进忽略清单。清理用 latexmk -c 一条命令搞定。但不要养成随手删的习惯——这些文件存在的意义就是替你省事,每次都清掉相当于每次都故意绕远路。只有在问题反复出现又找不出原因时,才动扫帚。
为什么要编译两次:?? 和 Rerun 警告
因为第一遍运行时,LaTeX 还不知道答案。 你写下「见第 1 节」时,LaTeX 要等到真正排出那一节之后才知道它的编号和页码——而引用几乎总是出现在被引对象之前。于是第一遍先按已知的部分排版,同时把答案写进 .aux;第二遍读回来,把空缺补上。目录也是同样的道理:\tableofcontents 在文档最前面,可它的内容要读到最后才清楚。下面是这两遍的真实记录。
$ pdflatex ref.tex # first run, from a clean directory
No file ref.aux.
No file ref.toc.
LaTeX Warning: Reference `sec:intro' on page 1 undefined on input line 5.
LaTeX Warning: There were undefined references.
LaTeX Warning: Label(s) may have changed. Rerun to get cross-references right.
Output written on ref.pdf (1 page, 33009 bytes).
# the PDF now reads: "See Section ?? on page ??."
$ pdflatex ref.tex # second run
Output written on ref.pdf (1 page, 34613 bytes).
# the PDF now reads: "See Section 1 on page 1."有三处值得留意。第一遍写着 No file ref.aux.——便条本还不存在。引用被印成 ??,并给出 LaTeX Warning: Label(s) may have changed. Rerun to get cross-references right.。「Rerun」这个词本身就是指令:再跑一次。第二遍警告消失,?? 变成了 1。目录也一样:第一遍只印出标题、内容是空的,到第二遍才排出条目。PDF 里出现 ??,不是坏了,而是在要求你再编译一次。
嫌数次数麻烦,就交给 latexmk。在干净目录里执行 latexmk -pdf ref.tex,它会打印 Run number 1 of rule 'pdflatex'、Run number 2 of rule 'pdflatex',然后是 Latexmk: All targets (ref.pdf) are up-to-date——恰好跑完所需的两遍就停下。用上参考文献(BibTeX/biber)和索引(makeindex)后需要的次数还会增加,这些也归 latexmk 管。VS Code 的 LaTeX Workshop、TeXShop、Overleaf 通常本来就是在背后调用 latexmk。
打出来不是你想要的字符:%、&、_、#、$
LaTeX 里有十个字符,直接打出来会变成别的意思:# $ % & ~ _ ^ \ { }。第一天最容易扎到人的是 %,而它之所以扎人,是因为它根本不报错。写下 Only 50% of the sample survived.,下一行接 The rest did not.,PDF 上印出来的是「Only 50The rest did not.」。从 % 到行尾的内容被当作注释丢掉,消失的行尾让下一行直接接到了同一段里。没有报错,所以毫无提示——而百分数在科技写作里到处都是。正确写法是 50\%。
| 字符 | 直接打会怎样 | 要打印它该怎么写 |
|---|---|---|
% | 该行剩下的部分被当注释悄悄丢掉。不报错 | \% |
$ | 开启或结束数学模式;只写一个,后面的内容全被拖进公式 | \$ |
& | 表格与对齐环境的列分隔符;在正文里会得到 ! Misplaced alignment tab character &. | \& |
_ | 数学中的下标;在正文里会给出 ! Missing $ inserted. | \_ |
^ | 数学中的上标;在正文里和 _ 一样给出 ! Missing $ inserted. | \textasciicircum{} |
# | 宏的参数记号;会得到 ! You can't use ... in horizontal mode. | \# |
~ | 不可断行的空格(如 Fig.~1);它本身不会作为字符印出来 | \textasciitilde{} |
\ | 命令的起点;紧随其后的内容会被当作命令名 | \textbackslash |
{ } | 界定参数与分组;不会出现在输出里 | \{ 和 \} |
还有两个毛病,正因为不报错而格外麻烦。第一是命令会吃掉后面的空格。写 \LaTeX is a macro package.,出来的是「LATEXis a macro package.」,因为 LaTeX 为了判断命令名到哪里结束,会把后面的空格吞掉。用空的花括号 \LaTeX{} is,或者写成 \LaTeX\ is 就能解决。第二是引号:打 "hello",两端都会变成右引号(”hello”)。左引号是两个反引号,右引号是两个撇号,正确写法是 ``hello''。
读懂最初的报错:! Missing $ inserted. 之类
只需要看以 ! 开头的第一行,以及紧随其后以 l. 开头的那一行。 l. 是 line 的缩写,后面的数字是行号,该行的内容也会一并印出,而且恰好在 TeX 绊倒的位置断成两行。断点就是案发现场。下面是在正文里写了 x_1 之后的输出。由于 TeX 会在第一个错误之后硬着头皮继续,后面排着的那些错误多半是连锁反应。只修最上面那一个,然后重跑。
$ pdflatex e2.tex # line 3 of the source reads: The value of x_1 is small.
! Missing $ inserted.
<inserted text>
$
l.3 The value of x_
1 is small.
$ pdflatex e3.tex # line 3 reads: Smith & Jones wrote it.
! Misplaced alignment tab character &.
l.3 Smith &
Jones wrote it.
$ pdflatex sc.tex # line 3 reads: Issue #42 and more.
! You can't use `macro parameter character #' in horizontal mode.
l.3 Issue #
42 and more.
$ pdflatex e5.tex # \begin{itemize} was never closed
! LaTeX Error: \begin{itemize} on input line 3 ended by \end{document}.
$ pdflatex e4.tex # \end{document} is missing entirely
*** (job aborted, no legal \end found)
! ==> Fatal error occurred, no output PDF file produced!! Missing $ inserted. 的意思是「只能在公式里用的东西出现在了正文,于是 TeX 替你补了一个 $」。原因几乎总是 _ 或 ^:要么把它写成真正的公式 $x_1$,要么转义成 x\_1。! LaTeX Error: \begin{itemize} on input line 3 ended by \end{document}. 是环境没关,它算是比较友善的一类,因为它直接告诉你环境是在哪一行打开的。看上去最吓人的 ! ==> Fatal error occurred, no output PDF file produced!,多半只是忘了写 \end{document}。而 ! Undefined control sequence. 要么是拼错了(\sectoin),要么是忘了加载提供该命令的宏包。
加上标题和小节,把它变成一份报告
到这一步,剩下的只是往上加。把 \title、\author、\date 写进序言区,在正文开头调用 \maketitle,标题就排出来了。用 \section、\subsection 建立小节,编号会自动生成;放上 \tableofcontents 就会自动生成目录(如前所述,目录要到第二遍才出现)。\date{\today} 会被替换成编译当天的日期;不想编号的小节,加个星号写成 \section*{...}。
\documentclass{article}
\title{My First Report}
\author{Taro Yamada}
\date{\today}
\begin{document}
\maketitle
\tableofcontents
\section{Introduction}
Blank lines start new paragraphs. Line breaks in the source do not.
\section{Method}
\subsection{Setup}\label{sec:setup}
Only 50\% of the sample survived. See Section~\ref{sec:setup}.
\end{document}这个例子里还塞进了写正文时最重要的一条规则:源码中的换行会被忽略,空行才是段落分隔。 你每句一换行,或者连空三行,输出都一样。最终的折行位置由 LaTeX 通盘看完整段之后决定。只有真的想另起一段时,才插入空行。
用宏包扩充功能:\usepackage
缺什么功能就用宏包补,办法只是在序言区加一行 \usepackage{...}。插图用 graphicx,正经排公式用 amsmath,改页边距用 geometry,做链接和 PDF 书签用 hyperref。TeX Live 之类的发行版随包附带成千上万个宏包,它们的总仓库叫 CTAN(Comprehensive TeX Archive Network)。但别一上来就堆一堆。 只留下你能说出理由的那些:当两个宏包打架、冒出 Option clash for package ... 之类的错误时,排查的麻烦程度和你加载的数量成正比。
\documentclass[a4paper,11pt]{article}
\usepackage{graphicx} % include images
\usepackage{amsmath} % proper math environments
\usepackage[margin=25mm]{geometry} % page margins
\usepackage{hyperref} % links and PDF bookmarks; load it last
\begin{document}
\section{Results}
Text, images and equations go here.
\end{document}按惯例 hyperref 要放在最后加载。它的工作方式是改写别的宏包的命令,所以加载得太早,后来的宏包会把它的改动覆盖掉。到这里为止的内容——最小文档、序言区与正文的分界、编译、辅助文件、第二遍运行、特殊字符,以及最初的报错——就是第一天需要的全部。接下来,写你真正想写的东西,需要什么功能再一样样加上去就够了。