场景解决方案2026年7月28日

LaTeX 写中文乱码、字体找不到?先确认你用的是哪个引擎

中文 LaTeX 的绝大多数报错都不在宏包,而在引擎:pdfLaTeX 的字体模型天生装不下几千个汉字。本文讲清三代中文方案的差异、ctex 的 fontset 该怎么选,以及 font not found 和 ctexart.cls not found 各自的成因。

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

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-basicscheme-small 体积小,但不包含 ctex;scheme-full 才是全量。很多人为了省空间装了精简版,结果一写中文就撞上这个错。

补装即可:

bashtlmgr install ctex

同理还可能缺 xecjkzhnumberfandolcjkpunct 这几个包——它们是中文链条上的常见依赖。顺带一提,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,这一个习惯能省掉后面大量的「在我这能编」的扯皮。

本文用到的工具

常见问题

大概率是用 pdfLaTeX 编译的。pdfLaTeX 沿用 8-bit 字体模型,一个字体最多 256 个字形,装不下几千个汉字,也不能直接调用系统里的中文字体。把编译命令换成 xelatex(并加载 ctex 宏包)通常就能解决。