中文 LaTeX 的报错看起来五花八门——空白、乱码、
font not found、ctexart.cls not found——但根源常常只有一个:引擎选错了。搞清楚 pdfLaTeX 和 XeLaTeX 的区别,一大半问题会自己消失。

为什么同样的代码,别人能出中文我却不行
因为你们用的编译引擎不一样。这是中文 LaTeX 最大的信息差:同一份 .tex,用 pdflatex 编译和用 xelatex 编译,是两条完全不同的字体处理路径。
pdfLaTeX 继承自 1980 年代的 TeX,用的是 8-bit 字体模型:一个字体文件最多提供 256 个字形。对拉丁字母绰绰有余,但中文常用字就有几千个,根本装不进去。早年的解决办法(CJK 宏包)是把汉字切成几十个 256 字形的子字体再拼回去,能用,但配置繁琐、字体切换容易出错,遇到没预处理过的字体就没辙。
XeLaTeX 和 LuaLaTeX 是后来重写的引擎,原生支持 Unicode,并且能直接调用操作系统里安装的 OpenType/TrueType 字体。中文对它们来说和英文没有本质区别,都是 Unicode 码位加字形。所以现在写中文的标准答案就一句话:用 xelatex 编译,配 ctex 宏包。
三个引擎处理中文的区别
| 引擎 | 字体来源 | 中文方案 | 现在还该用吗 |
|---|---|---|---|
| pdfLaTeX | TeX 专用的 8-bit 字体(TFM/Type1) | CJK / CJKutf8,需切分子字体 | 不推荐,仅为兼容老模板 |
| XeLaTeX | 系统安装的 OpenType/TrueType | xeCJK(ctex 默认走这条) | 推荐,中文生态最成熟 |
| LuaLaTeX | 系统字体,另有 Lua 扩展能力 | luatexja | 可用,编译更慢但可编程性强 |
如果你从网上抄来的模板要求 pdflatex,而你又要加中文,最省事的做法通常不是去修字体配置,而是改用 xelatex 编译同一份文档——大多数常规模板换引擎后能直接跑通。
编译命令怎么改
命令行直接换程序名:
bashxelatex main.tex
编辑器里则要改默认编译链,这一步最容易被漏掉:
- VS Code + LaTeX Workshop:在设置里把
latex-workshop.latex.recipe.default指向含 xelatex 的 recipe,或者更省事——在.tex文件第一行写魔法注释% !TEX program = xelatex; - TeXstudio:选项 → 设置 → 构建,把「默认编译器」改成 XeLaTeX;
- latexmk:加参数
latexmk -xelatex main.tex。
改完记得把之前 pdfLaTeX 留下的 .aux、.fls 等中间文件删掉,换引擎后旧缓存有时会带来莫名其妙的报错。
ctex 的 fontset 到底在选什么
fontset 选的是一整套中文字体的映射方案——宋体、黑体、楷体、仿宋分别对应到你机器上的哪个具体字体文件。写法是:
latex\documentclass{ctexart}
\usepackage[fontset=fandol]{ctex}
常见取值和各自的代价:
| fontset | 实际用的字体 | 前提条件 | 适合场景 |
|---|---|---|---|
windows |
中易宋体 / 黑体 / 楷体等 | 必须在 Windows 上,且系统装了这些字体 | 只在自己 Windows 机器上编译 |
mac |
华文系列 / 苹方 | 必须在 macOS 上 | 只在 Mac 上编译 |
ubuntu |
文泉驿等开源字体 | 系统装了对应字体包 | Linux 桌面环境 |
fandol |
Fandol 开源字体 | 随 TeX Live 分发,无需系统字体 | 跨机器、投稿、CI 编译 |
none |
不做任何预设 | 需自己用 fontspec 全部手动指定 | 期刊模板已规定字体时 |
不写 fontset 时,ctex 会尝试自动探测当前操作系统。这在自己电脑上很方便,但也正是「我这能编,发给同学就报错」的根源。
font not found 是怎么回事
因为 fontspec 找的是系统安装的字体名,而不是 TeX 目录里的文件。当你写 fontset=windows,ctex 实际上是在让引擎去问操作系统:「有没有一个叫 SimSun 的字体?」Windows 上有,Linux 服务器和 GitHub Actions 上没有,于是报错。
这类报错的典型形态是引擎抱怨某个字体名无法解析,或者干脆提示字体文件不存在。判断方法很简单:看它报的名字是不是一个系统字体名。如果是,问题就出在这台机器上没装它,而不是你的 LaTeX 代码写错了。
所以有一条实用建议:要发给别人、要投稿、要在 CI 上编译的稿子,一律用 fontset=fandol。 Fandol 是随 TeX Live 一起分发的开源中文字体,不依赖任何系统字体,在哪台机器上编译结果都一样。自己本地写着玩,用系统字体更好看也无妨。
ctexart.cls not found 又是另一回事
ctexart.cls not found 和字体无关,是发行版装得不全。TeX Live 安装时可以选 scheme:scheme-basic 和 scheme-small 体积小,但不包含 ctex;scheme-full 才是全量。很多人为了省空间装了精简版,结果一写中文就撞上这个错。
补装即可:
bashtlmgr install ctex
同理还可能缺 xecjk、zhnumber、fandol、cjkpunct 这几个包——它们是中文链条上的常见依赖。顺带一提,fontspec 是 xeCJK 的硬依赖,如果连它都缺,说明发行版精简得比较狠,不如直接补一个更完整的 scheme。
不想在本地折腾这套依赖时,浏览器里也能跑:在线 LaTeX 编辑器 的高级模式是编译成 WebAssembly 的 XeLaTeX,ctex、xeCJK、Fandol 字体都已经打包在里面,\documentclass{ctexart} 拖进去直接就能编译,省掉了选 scheme 和补包这一步。
边界:换对引擎也解决不了的几种情况
生僻字缺字形。 Fandol 覆盖的是常用汉字(大致相当于 GB 2312 的范围),古籍用字、生僻姓名字、部分繁体字可能没有对应字形,输出会是空白或方框。这种情况需要换一个字符集更大的字体(如思源宋体),用 \setCJKmainfont 手动指定。
期刊模板自带字体规定。 不少中文期刊的 .cls 已经写死了字体配置,此时再加 ctex 的 fontset 可能冲突。优先按模板文档的说明来,别自作主张覆盖。
中英文混排的间距细节。 xeCJK 默认会在中西文之间插入一点间距,多数时候是对的,但公式密集的段落里偶尔需要用 \xeCJKsetup 微调。这属于精修阶段的事,不影响能不能编译。
小结
中文 LaTeX 的排查顺序是固定的:先确认引擎是不是 xelatex,再确认 ctex 装没装,最后才看 fontset 选得对不对。 三步走完,绝大多数「中文出不来」的问题都能定位。要给别人的稿子记得用 fontset=fandol,这一个习惯能省掉后面大量的「在我这能编」的扯皮。