用 matplotlib 画图,最让人血压飙升的瞬间莫过于——图是画出来了,线啊点啊都对,可所有中文标签全变成一个个方框,或者干脆消失。更气人的是,在 PyCharm 里跑没问题,换个 VSCode 终端就乱码,自己电脑好好的一拿到服务器就废。我前前后后踩过不下二十次这种坑,今天把根治方案、排查思路和避坑经验一次说透。无论是刚接触数据可视化的新手,还是被中文折磨已久的写论文人、做报表的职场人,这篇文章都能帮你彻底告别 matplotlib 中文乱码。
1. 中文乱码的本质:不是 matplotlib 的问题,而是字体与编码的错配
1.1 乱码到底是怎么发生的
很多人一遇到乱码就想去重装 matplotlib,其实这跟 matplotlib 本身没什么关系。Matplotlib 默认使用的字体是 DejaVu Sans,这是一套西文字体,里面压根就没有中文字形。当绘图文本里出现“销售数据”“柱状图”这类汉字时,matplotlib 在字体库中找不到对应的字形,于是只能用一个空心方框来代替。Windows 下还会出现一种情况,就是字符被错误解析成乱码符号,比如一堆“□□□□”或者“锟斤拷”,后者其实是编码转换错误导致的经典乱码。
明白了这个原理,解决路径就清晰了:让 matplotlib 使用一款支持中文的字体。常见的可选方案有三种,一是全局修改 rcParams 字体配置,二是安装额外的中文字体到系统,三是动态指定字体文件路径。大部分人推荐第一种,确实也是最稳定的一种。但具体怎么配置、配完为什么还不生效,这里面的门道很多,我下面会逐一拆解。
1.2 字体查找机制:matplotlib 怎么决定用哪个字体
在进入实操前,有必要先了解一下 matplotlib 的字体查找流程,不然遇到findfont: Font family 'SimHei' not found这种报错时还是会一脸懵。
当调用plt.xlabel('年份')时,matplotlib 会进入字体管理器,它先读取当前 rcParams 中font.family和font.sans-serif的配置,然后在系统字体缓存里匹配对应的 TTF/TTC 文件。如果找不到,就会把警告打到终端,并回退到默认字体。注意,这里的“找到”指的是字体文件能被 matplotlib 的字体管理器正常加载,而不是只要系统里装了微软雅黑就万事大吉。某些第三方发行版(比如通过 conda 安装的 matplotlib)可能没有继承系统字体路径,导致明明装了中文字体却仍然乱码。
1.3 对比表格:常见中文字体的适配性
为了让你一目了然地选择,我整理了一个常用中文字体在 matplotlib 中的适配情况表,环境是 Windows 10 + Python 3.10 + matplotlib 3.7。
| 字体名称 | 系统自带情况 | matplotlib 中常见写法 | 小字号清晰度 | 是否容易缺字 |
|---|---|---|---|---|
| SimHei(黑体) | Windows 自带 | SimHei | 较好 | 偶尔 |
| SimSun(宋体) | Windows 自带 | SimSun | 好 | 少见 |
| Microsoft YaHei(微软雅黑) | Windows 自带 | Microsoft YaHei | 非常好 | 极少数生僻字 |
| KaiTi(楷体) | Windows 自带 | KaiTi | 好 | 少见 |
| Noto Sans CJK SC | 需下载 | Noto Sans CJK SC | 非常好 | 无 |
| WenQuanYi Zen Hei | Linux 常见 | WenQuanYi Zen Hei | 好 | 无 |
| Source Han Sans CN(思源黑体) | 需下载 | Source Han Sans CN | 非常好 | 无 |
从经验来看,日常出图优先SimHei或Microsoft YaHei,如果要提交期刊或打印,推荐思源黑体,因为它的字形更规范和统一。这里要特别提醒:字体名称必须严格按照 matplotlib 识别的名字来写,不是你在 Word 里看到的名字。比如系统里安装的是“方正小标宋简体”,matplotlib 可能识别不了这种带修饰词的名字,这时就需要用font_manager.fontManager.ttflist查看实际名字。
2. 三种常用解决方案:从永久生效到临时指定
2.1 方案一:全局修改 rcParams(最常用、最实用)
这是最正宗的写法,只要在绘图脚本的最前面加上几行代码,所有文本都会使用中文字体。我一般是这样写的:
import matplotlib.pyplot as plt plt.rcParams['font.sans-serif'] = ['SimHei'] # 指定默认无衬线字体为黑体 plt.rcParams['axes.unicode_minus'] = False # 解决负号‘-’显示为方块的问题第一行很好理解,就是告诉 matplotlib 用黑体渲染文字。第二行axes.unicode_minus设置成False,是为了让坐标轴的负号使用 ASCII 短横线,而不是 Unicode 的减号。为什么必须设置?因为在某些中文字体下,Unicode 减号字形是缺失的,会显示成小方块。这个问题很容易被忽略,导致坐标轴出现“-0.5 变成 □0.5”的怪现象。
完整的使用示例:
import matplotlib.pyplot as plt plt.rcParams['font.sans-serif'] = ['SimHei'] plt.rcParams['axes.unicode_minus'] = False days = ['周一', '周二', '周三', '周四', '周五'] temps = [23, 25, 27, 26, 24] plt.plot(days, temps, marker='o') plt.title('一周温度变化') plt.xlabel('星期') plt.ylabel('温度(℃)') plt.show()这段代码直接跑起来,标题、坐标轴标签就正常了。有一点要注意,font.sans-serif这个键对应的值是一个列表,你可以同时列出多个备选字体,比如['Microsoft YaHei', 'SimHei', 'Arial'],matplotlib 会从左到右依次尝试,直到找到可用的为止。这样即使某个字体在某些系统上不存在,也会自动降级。
2.2 方案二:使用字体管理器动态注册字体(适合无中文环境)
当你把脚本扔到服务器上跑,而服务器是纯 CentOS 系统,往往没有安装任何中文字体,此时方案一就会失效。我们可以在脚本中动态下载并注册一个字体文件,无需系统级安装。第一步,准备一个 TTF 文件,比如从我常用的路径拷贝:
import matplotlib import matplotlib.font_manager as fm # 字体文件路径,TTF或OTF均可 font_path = '/path/to/your/NotoSansCJKsc-Regular.otf' prop = fm.FontProperties(fname=font_path)然后在使用时,把FontProperties对象传给每个需要中文的绘图接口:
import matplotlib.pyplot as plt # 使用上面的 prop plt.plot(['中文', '数据'], [1, 2]) plt.title('自定义字体', fontproperties=prop) plt.xlabel('类别', fontproperties=prop) plt.ylabel('数值', fontproperties=prop)这种方式的优势是,即使电脑上没有任何系统中文,只要脚本里带一个字体文件,就能在任何机器上稳定复现。缺点是每次设置文本都要加fontproperties参数,代码冗余。如果你想全局生效,可以这样注册:
import matplotlib.font_manager as fm import matplotlib.pyplot as plt fm.fontManager.addfont('/path/to/your/NotoSansCJKsc-Regular.otf') plt.rcParams['font.family'] = 'Noto Sans CJK SC'addfont方法在 matplotlib 3.2 之后存在,低于这个版本的也可以手动构建FontProperties再添加到fontManager.ttflist,不过更建议升级到新版。
2.3 方案三:临时修改字体设置(单图快速调试)
有时候只是临时画个示意图,不愿意动全局配置,可以在单个 Figure 对象上临时设置:
import matplotlib.pyplot as plt from matplotlib.font_manager import FontProperties # 读取系统字体 font_path = 'C:/Windows/Fonts/msyh.ttc' # Windows 微软雅黑 my_font = FontProperties(fname=font_path, size=12) fig, ax = plt.subplots() ax.set_title('临时图', fontproperties=my_font) ax.set_xlabel('X 轴', fontproperties=my_font)这一种最灵活,但如果你要写一个有几十个图标的论文,每一个轴都要去指定一遍,绝对会怀疑人生。所以我个人的经验是:写脚本时用方案一的全局配置打底,遇到特殊字体需求再叠加方案二的动态注册。方案三只在快速试错时用。
2.4 三种方案的适用场景对比
| 方案 | 优点 | 缺点 | 适用环境 |
|---|---|---|---|
| 全局 rcParams | 简洁、一行搞定 | 依赖系统已安装字体 | 个人电脑、本地脚本 |
| 动态注册 TTF | 跨平台、可移植 | 需要携带字体文件 | 服务器、云端、共享脚本 |
| 单图 FontProperties | 精准控制 | 代码冗长,易遗漏 | 快速调试、单个特殊图 |
3. 实操全流程:从检测到修复,一步不落
3.1 先检查当前环境到底缺什么
不要一乱码就照着网上的代码粘贴,第一步应该是定位。打开 Python 终端,执行以下代码查看系统字体列表:
from matplotlib import font_manager fonts = [f.name for f in font_manager.fontManager.ttflist] print(sorted(set(fonts)))如果输出结果里没有SimHei、Microsoft YaHei这类中文字体,说明当前 Python 环境根本没有扫描到中文,那就需要检查系统是否安装中文字体,或者使用动态注册。如果列表里有中文字体但还是乱码,那问题多半出在代码没有正确指定,比如plt.rcParams['font.sans-serif'] = 'SimHei'这种错误赋值——注意这里应该是一个列表,字符串后面没有加方括号,matplotlib 也会报错。
另外还要查一下 matplotlib 版本,老版本(2.x 及以前)对字体支持不同,建议升级到最新版:
pip install -U matplotlib升级之后再次运行检测脚本,往往一些莫名其妙的问题就消失了。
3.2 完整修复示例:一个带中文的散点图
假设我们要绘制一个“各季度销售额散点图”,直接上完整代码,这是能直接复现的:
import matplotlib.pyplot as plt # 1. 全局字体设置 plt.rcParams['font.sans-serif'] = ['Microsoft YaHei', 'SimHei', 'Arial'] plt.rcParams['axes.unicode_minus'] = False # 2. 数据 quarters = ['Q1(一季度)', 'Q2(二季度)', 'Q3(三季度)', 'Q4(四季度)'] sales = [125, 180, 210, 165] # 3. 绘图 plt.figure(figsize=(8, 5)) plt.scatter(quarters, sales, s=80, c=['#1f77b4', '#ff7f0e', '#2ca02c', '#d62728']) plt.title('各季度销售额散点图') plt.xlabel('季度') plt.ylabel('销售额(万元)') plt.grid(True, linestyle='--', alpha=0.5) plt.show()假如这里运行出来仍然有小方块,大概率是你的Microsoft YaHei字体文件名是msyh.ttc,matplotlib 对 TTC 格式的兼容性略差。解决办法是换用SimHei或者下载单独的 TTF 格式字体。Windows 自带的黑体是simhei.ttf,一般都能被正常识别。
3.3 字体文件放在哪更合适
如果你走的是动态注册路线,字体文件的存放位置也有讲究。不要随便丢在临时目录,否则脚本换台机器就跑不动。建议在项目里建一个fonts/目录,把字体文件统一放在里面,然后通过绝对路径引用。如果你的脚本要给别人用,还可以用os.path.join拼接路径,增强可移植性:
import os import matplotlib.font_manager as fm base_dir = os.path.dirname(os.path.abspath(__file__)) font_path = os.path.join(base_dir, 'fonts', 'SimHei.ttf') fm.fontManager.addfont(font_path)这样做的好处是,整个项目目录拷贝到任何机器上都能运行。我习惯在团队里统一维护一个fonts文件夹,里面放思源黑体的几个字重文件,这样大家画出来的图风格一致。
3.4 确认修复成功:如何快速判断
修复后,不要只看图是否正常,还要注意看终端有没有警告。如果字体匹配失败,matplotlib 会打印类似findfont: Font family 'SimHei' not found的警告。输出图形正常但终端有警告,说明这次虽然显示了,但下一次可能又找不到。可以写一个自定义字体管理器方法:
import warnings import matplotlib.pyplot as plt with warnings.catch_warnings(): warnings.simplefilter("error") plt.rcParams['font.sans-serif'] = ['SimHei']这样可以将警告升级为异常,方便在 CI 中检测问题。
4. 中文乱码延伸出来的两个高频场景:负号与子图图例
4.1 负号显示成方框的解决技巧
这个我在前面提了一嘴,但值得单独说。很多人设置完中文字体后,正数正常,一遇到负数坐标就显示为方框。原因是轴的刻度默认使用 Unicode 字符 U+2212 作为负号,而这个字符在许多中文字体中不存在。解决办法就是全局设置:
plt.rcParams['axes.unicode_minus'] = False设置为 False 之后,matplotlib 会改用 ASCII 连字符(-),这样就不会受中文字体限制。如果你不想改全局,也可以在坐标格式器上设置:
import matplotlib.ticker as ticker ax.yaxis.set_major_formatter(ticker.FormatStrFormatter('%g'))这会把负号也转成普通 ASCII 字符,道理相同。不过我始终推荐改 rcParams,简单且不易遗漏。
4.2 图例和子图中的中文怎么处理
图例乱码和标题乱码原理完全一致,但处理时有一个小坑:如果你使用了label参数,并且全局字体设置生效,图例就会正常;但如果你手动创建了单独的图例对象,可能还要重新指定字体:
plt.legend(prop={'family': 'SimHei'})对于子图,很多人会在plt.subplots()后忘记设置字体,导致每个子图的标题和刻度乱码。最保险的做法是使用plt.rcParams全局设置后,子图不需要额外操作。但要注意,如果你在一个脚本中混用了不同字体,子图之间可能会互相干扰,最好用一个命名规范。比如:
fig, axes = plt.subplots(2, 2, figsize=(10, 8)) axes[0, 0].plot(...) axes[0, 1].plot(...)这里只要开头设置了 font.sans-serif,所有子图的标题、坐标轴都会正常,不需要每个轴都设置。
4.3 跨平台脚本的字体兼容方案
如果你要在 Windows、macOS、Linux 三端运行同一套绘图脚本,直接写死SimHei在 macOS 上就会失效。这就需要做平台判断:
import platform import matplotlib.pyplot as plt system = platform.system() if system == 'Windows': font_list = ['Microsoft YaHei', 'SimHei'] elif system == 'Darwin': font_list = ['PingFang SC', 'Heiti SC', 'Songti SC'] else: font_list = ['Noto Sans CJK SC', 'WenQuanYi Zen Hei'] plt.rcParams['font.sans-serif'] = font_list plt.rcParams['axes.unicode_minus'] = False这是一个万金油配置,虽然不能保证每个系统都完美,但至少不会出大毛病。在 macOS 上,PingFang SC是系统默认中文字体,matplotlib 一般能识别。Linux 上如果没有Noto Sans CJK SC,需要安装fonts-noto-cjk包,或者用方案二动态加载字体文件。
5. 常见问题与排查技巧实录
5.1 速查表:乱码场景与解决路径
| 现象 | 原因 | 解决方案 |
|---|---|---|
| 中文显示为空心方框 | 默认字体不含中文字形 | 设置font.sans-serif为中文字体 |
| 负号显示为方框 | Unicode 减号缺失 | 设axes.unicode_minus=False |
| 终端有 findfont 警告 | 指定字体不存在 | 检查字体名称或动态 addfont |
| 在 PyCharm 正常,终端乱码 | 环境变量不同或字体缓存未刷新 | 重启 kernel,清空 matplotlib 缓存~/.matplotlib |
| 设置后仍然乱码 | 配置文件 rcParams 被后置代码覆盖 | 搜索代码中是否还有plt.rcParams赋值 |
Font family not found报错 | 字体名拼写错误 | 用fontManager.ttflist核对准确名称 |
| 复制到服务器上乱了 | 服务器系统无中文字体 | 动态加载 TTF 或安装字体 |
5.2 细节坑:配置被覆盖和缓存遗忘
比较隐蔽的一个情况是,你在 A 文件开头设置了plt.rcParams['font.sans-serif']=['SimHei'],但后来在 B 文件或同一个脚本后面又调用了plt.style.use('seaborn')之类的样式,这个样式会重置 rcParams,导致中文字体设置失效。解决方法是把字体设置放在plt.style.use之后,或者使用plt.rcParams.update({'font.sans-serif': ['SimHei']})强制更新。
还有缓存问题,matplotlib 首次运行时会扫描系统中所有字体,生成一个缓存文件在用户目录的.matplotlib文件夹下。当新装了中文字体后,matplotlib 仍然使用旧缓存,导致识别不到。解决方法:
rm -rf ~/.matplotlib或者直接用 Python 删:
import matplotlib import shutil cache_dir = matplotlib.get_cachedir() shutil.rmtree(cache_dir)清掉之后,下次运行会自动重建缓存,新字体就被识别了。这个操作非常简单,但能解决一大批“明明安装了字体却不生效”的案例。
5.3 进阶:将中文字体嵌入 PDF/SVG 文件
如果你不是只在屏幕上看看,而是要把图片保存后发给别人,还要注意保存图片时是否嵌入了字体。SVG 格式默认会把字体路径记录下来,别人打开时如果系统没有对应字体,可能在他们的电脑上显示乱码。解决办法是在保存时指定字体嵌入方式:
plt.savefig('output.svg', format='svg', bbox_inches='tight')对于 SVG,matplotlib 生成的路径会通过字体对象来引用文本,通常编辑器能自动处理。更稳妥的是输出高分辨率的 PNG,并使用dpi参数:
plt.savefig('output.png', dpi=300, bbox_inches='tight')300 DPI 足够印刷,而且渲染时就固定了字形,不会受到查看端字体影响。这是我的惯用做法,出论文图基本不会出错。
5.4 谈起乱码时的额外提醒:CSV 数据源中文乱码
有时候图中的中文标签本身是从文件读入的,比如读取csv文件的中文列名,如果 pandas 读进来就已经是乱码,那 matplotlib 表现出的乱码其实是数据源头的问题。排查时要先打印输出字符串看看是否正确。常见的文件编码问题要用对参数:
import pandas as pd df = pd.read_csv('data.csv', encoding='utf-8')如果文件是 GBK 编码:
df = pd.read_csv('data.csv', encoding='GBK')这一层不解决,就算 matplotlib 字体设得再好,图上也只会显示你读进来的乱码字符。所以遇到中文乱码,先分清是“字体导致的方框”还是“数据源导致的乱码”,两者的处理路径完全不同。
6. 工具选型推荐:中文字体选择与编辑器配置
6.1 系统字体怎么看、怎么选
Windows 下打开C:\Windows\Fonts,能看到simhei.ttf、msyh.ttc、simsun.ttc等文件。建议在项目字体文件夹中保留SimHei.ttf和msyh.ttc的副本,但要注意微软雅黑的 TTC 文件在部分 matplotlib 版本中可能加载失败,如果遇到问题,就换用黑体。Linux 下可用fc-list :lang=zh查看已安装中文字体:
fc-list :lang=zh如果输出为空,先安装:
sudo apt install fonts-noto-cjkmacOS 可以在“字体册”里查看,一般系统自带PingFang.ttc,但 matplotlib 可能只识别为PingFang SC,需要单独测试。我的测试结论是 Matplotlib 3.6 之后对 macOS 的 PingFang 支持已经比较友好,但仍有偶发警告。
6.2 推荐使用的开源免费字体
如果你的需求是公开出版或者商业用途,务必谨慎。微软雅黑等字体有商业授权限制,更适合用开源字体。我最常用的开源字体有两个:思源黑体(Source Han Sans SC)和文泉驿正黑(WenQuanYi Zen Hei)。思源黑体的字形现代、清晰度高,包含简体/繁体/日文/韩文等多字重,最推荐。从 GitHub 下载后解压,把 TTF 或 OTF 文件放在fonts/目录,再用fm.fontManager.addfont()注册即可。
字体下载后要检查大小,正规思源黑体单个 OTF 文件大约 15 MB 左右,如果某个网站给你的文件只有几百 KB,那多半是裁剪版或者非官方版本,加载可能出问题。建议直接从官方 GitHub 仓库 release 页面下载。
6.3 IDE 与终端的中文输出配合
虽然本文主题是 matplotlib,但很多人在 IDE 的终端里输出中文也是乱码,这会影响调试。如果是在 Windows 的 VSCode 终端中运行 Python 脚本,输出中文全是乱码,可能不是 matplotlib 的问题,而是 Python 控制台编码问题。推荐在脚本开头加:
import sys import io sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')或者更稳妥地,将项目文件统一保存为 UTF-8 编码,并在系统区域设置中勾选“Beta: 使用 Unicode UTF-8 提供全球语言支持”。不过后者会影响整个系统,谨慎开启。对于仅调试,建议用环境变量:
set PYTHONIOENCODING=utf-8这样设置后,控制台输出的中文也能正常显示。注意,这类设置优先级高于脚本内的sys.stdout重写,更适合临时解决问题的场景。
7. 规避乱码的架构思维:把字体配置做成公共模块
7.1 写一个mpl_config.py公共配置模块
经验丰富之后,你不会在每个脚本里重复写字体配置,而是会把它抽成一个公共模块。比如项目中新建mpl_config.py:
# mpl_config.py import platform import matplotlib.pyplot as plt def setup_chinese_font(): system = platform.system() font_candidates = { 'Windows': ['Microsoft YaHei', 'SimHei'], 'Darwin': ['PingFang SC', 'Heiti SC'], 'Linux': ['Noto Sans CJK SC', 'WenQuanYi Zen Hei', 'SimHei'] } plt.rcParams['font.sans-serif'] = font_candidates.get(system, ['SimHei']) plt.rcParams['axes.unicode_minus'] = False然后在绘图脚本中调用:
from mpl_config import setup_chinese_font setup_chinese_font()这样新同事接手项目时,不需要去看冗长的配置代码,只需要调用一个函数,就能保证所有图表不出现中文乱码。在团队协作中,这种方式能极大减少“在哪儿配置字体”的沟通成本。
7.2 在绘图函数中使用字体参数复用
如果你的项目中有大量相似图表,可以统一使用自定义绘图函数,把字体配置封装进去:
def create_figure(title, x, y, xlabel, ylabel): setup_chinese_font() fig, ax = plt.subplots() ax.plot(x, y) ax.set_title(title) ax.set_xlabel(xlabel) ax.set_ylabel(ylabel) return fig, ax这样既保证了字体风格统一,也便于后续统一调整样式。真正把“解决乱码”这一步从每次画图前的重复劳动,变成了一次性的系统工程。
8. 结合热搜词补充:雷达图、散点图与图例中的中文细节
8.1 雷达图中的中文标签处理
雷达图常用于展示多维数据,比如团队能力评估、产品维度分析。我在画雷达图时就遇到过中文标签重叠和乱码叠加的问题。乱码部分还是老办法,全局设置字体即可。但要注意雷达图的刻度标签和角度标签都很多,建议把字体调小一点:
plt.rcParams.update({ 'font.size': 10, 'font.sans-serif': ['SimHei'] })同时用polar=True创建极坐标子图时,需要自己设置角度标签:
ax.set_xticks(np.linspace(0, 2*np.pi, len(labels), endpoint=False)) ax.set_xticklabels(labels)这里labels就是中文列表,只要字体设对,渲染完全正常。
8.2 散点图透明度与网格中的中文
散点图的中文乱码主要体现在图例或数据标签上。比如标注特定点,可以用annotate:
plt.annotate('异常点', xy=(x, y), fontproperties=prop)或者用ax.text。如果还想显示上标下标,比如“温度(℃)”里面的 ℃,就需要确保字体包含该符号。微软雅黑和思源黑体都支持。网格线的中文则很少涉及,但在图例中会有一个坑:直接设置label='中文标签'后如果还乱码,可以单独重设图例字体:
leg = ax.legend() for text in leg.get_texts(): text.set_fontname('SimHei')这种方法适合特殊情况下,图例文字和全局字体不相同时候强制覆盖。
9. 踩坑实录与个人心得
9.1 那次我在服务器上折腾了两小时
有一次我把本地跑得好好的数据脚本迁移到一台干净的 CentOS 服务器,画图时全是方框。当时第一反应是安装中文字体,于是登录 root 执行yum install -y fontconfig,然后手动下载simhei.ttf放到/usr/share/fonts/,fc-cache -fv刷新后,fc-list已经能看到 SimHei 了,但用 Python 运行时仍然报findfont: Font family 'SimHei' not found。最后才发现,CentOS 上的 matplotlib 是通过pip安装在虚拟环境里的,它没有读取系统的字体目录,而是依赖自己的字体缓存路径。我执行了rm -rf /root/.cache/matplotlib后,重新运行,它重新扫描到了系统字体,问题才真正解决。所以清缓存这个操作,比装字体还重要。
9.2 不要盲目复制网上的代码,先跑通最小案例
网上 90% 的教程会给你这段代码:
plt.rcParams['font.sans-serif'] = ['SimHei']但是在 Mac 上,SimHei 根本不存在,所以复制过来照样乱码。我的建议是,任何新环境先执行一个最小复现脚本:
import matplotlib.pyplot as plt plt.rcParams['font.sans-serif'] = ['SimHei', 'Microsoft YaHei', 'PingFang SC'] plt.rcParams['axes.unicode_minus'] = False plt.title('中文测试') plt.show()如果显示正常,再往大项目里移植;如果不正常,根据警告信息查问题,这样定位效率极高。盲目改代码只会让自己越改越乱。
9.3 利用font_manager自定义回退顺序优化体验
我还会直接创建一个回退字典,把不同字体的优先级拆分:
plt.rcParams['font.sans-serif'] = [ 'PingFang SC', 'Microsoft YaHei', 'SimHei', 'Noto Sans CJK SC', 'WenQuanYi Zen Hei' ]这样在 Mac 上用苹方,Windows 上用微软雅黑,Linux 上用 Noto,在不同系统上都有不错的显示效果。设置完成后,再调用一次检测函数:
from matplotlib import font_manager available = {f.name for f in font_manager.fontManager.ttflist} print([f for f in plt.rcParams['font.sans-serif'] if f in available])如果打印出来一个或几个可用字体,说明回退机制能正常工作。这一招很实用,尤其适合你写了平台自动切换逻辑的时候。
9.4 最后分享一个小技巧
如果图形上有需要手动标注的中文文本,可以用ax.annotate时单独指定fontproperties,但更省事的是在rcParams中设置默认字号和字体,然后依赖默认值。我在团队里常常把字体配置和绘图风格一起封装,例如:
import matplotlib.pyplot as plt plt.rcParams.update({ 'font.family': 'sans-serif', 'font.sans-serif': ['Noto Sans CJK SC', 'Microsoft YaHei'], 'axes.unicode_minus': False, 'figure.dpi': 120, 'savefig.dpi': 300, })这样一来,所有图形默认就是高清、中文正常、负号正常。关键是把这些配置写在一个独立的配置文件里,其他脚本直接import,而不是复制粘贴。
10. 扩展:从乱码到图表美观,还有这些细节值得做
中文不再乱码只是第一步,接下来很多刚入门的朋友会遇到字体大小、标签重叠、图例遮挡等问题,这些虽然不是“乱码”,但和字体设置息息相关。既然你已经把rcParams握在手里,不如顺手把图表美观性一起提升。
调整全局字号:
plt.rcParams.update({ 'font.size': 12, 'axes.titlesize': 16, 'axes.labelsize': 13, 'xtick.labelsize': 11, 'ytick.labelsize': 11, 'legend.fontsize': 10, })这个配置比每次单独设置fontsize高效得多。再配合tight_layout()或bbox_inches='tight',中文标签基本不会因为边界裁剪而消失。
另外,很多人会遇到保存的 PDF 中文字符变窄的问题,这是因为 PDF 引擎只嵌入字体子集,但 matplotlib 通常能正常嵌入。如果担心,可以选择保存为ps或直接通过矢量图工具二次转换。我的实践是,向期刊投稿时使用 600 DPI 的 TIFF,或者 EPS,但 EPS 对中文字体兼容性略差,所以还是以 PDF 优先。
从日常使用场景来说,如果你是学生交作业、工程师做调试,其实只要学会全局rcParams设置就足够应付 95% 的场景。剩下 5% 的奇葩环境,多看终端警告,多清缓存,多动态加载 TTF,也基本能解决。我先讲到这里,后面如果你们在实际操作中遇到更奇怪的乱码情况,欢迎按本文的排查思路一步步走一遍,大概率都能自己解决。