用 ggplot2 画图,标题和坐标轴里的中文全变成了小方框(或一串点)——这是用 R 画图最常遇到、也最容易踩坑的问题。网上教程一般会说"装个 showtext 就好了",但实际情况是:装了 showtext 依然乱码的人非常多,因为乱码的根源不是包,而是"图形设备 × 字体"的组合。本文先带你 60 秒定位自己是哪种情况,再按平台给出修复方案,最后是三个我实测踩到的坑。
先复现问题。下面这段代码在 macOS(R 4.3.1)上保存出来的图,标题和轴标签的中文全部变成了点框:
library(ggplot2)
p <- ggplot(mtcars, aes(x = wt, y = mpg)) +
geom_point() +
ggtitle("汽车油耗与车重的关系") +
xlab("车重(千磅)") + ylab("油耗(英里/加仑)")
ggsave("test.png", p, width = 6, height = 4, dpi = 150)

第一步:60 秒诊断,先看你的设备
乱码 = 当前图形设备找不到能画中文的字体。先在 R 里跑这三行,把结果记下来:
getOption("bitmapType") # png() 用的位图后端:quartz / cairo / windows
capabilities("cairo") # cairo 后端是否可用
packageVersion("ggplot2") # 版本(3.3+ 的 ggsave 可能默认走 ragg 设备)
它们决定了下面哪个方案对你有效——这也是"别人说 showtext 好使、实际却不好用"的根本原因。
方案 A:showtext 标准三件套(Windows / Linux / RStudio 通用)
install.packages("showtext")
library(showtext)
# 直接连网加载思源黑体,完全不依赖系统字体路径,三平台效果一致
font_add_google("Noto Sans SC", "notosans")
showtext_auto()
showtext_opts(dpi = 150) # 注意:必须和 ggsave 的 dpi 一致,见坑 2
p2 <- p + theme(text = element_text(family = "notosans"))
ggsave("test_fixed.png", p2, width = 6, height = 4, dpi = 150)

如果不想联网下载字体,也可以用 font_add() 直接注册字体文件,比如 Windows 上 font_add("yahei", regular = "C:/Windows/Fonts/msyh.ttc")。
方案 B:macOS 原生字体名(不装包)
macOS 的绘图窗口和 RStudio 走系统字体渲染,很多时候不用 showtext,直接指定系统字体名即可:
p + theme(text = element_text(family = "PingFang SC")) # 苹方
# 或 family = "Hiragino Sans GB"(冬青黑)
坑 1:硬编码的字体路径在新版 macOS 上不存在
网上教程常写 font_add("pingfang", "/System/Library/Fonts/PingFang.ttc"),我实测直接报错:
Error in check_font_path(regular, "regular") :
font file not found for 'regular' type
原因是这个路径在新版 macOS 上已经不在了。实测机器上(macOS arm64)真实存在的中文字体是:
/System/Library/Fonts/Hiragino Sans GB.ttc # 冬青黑体
/System/Library/Fonts/STHeiti Light.ttc # 华文黑体
/System/Library/Fonts/Supplemental/Songti.ttc # 宋体
系统字体路径随版本变动,所以更稳的做法是方案 A 的 font_add_google()——字体文件由 showtext 自己管理,跟系统无关。
坑 2:showtext_opts(dpi) 和 ggsave(dpi) 不一致,字会变大或变小
修完乱码后如果发现"字怎么突然变小/变大了",多半是这两处 dpi 没对上。下面是 showtext_opts(dpi = 72) 搭配 ggsave(dpi = 150) 的实测效果(字号明显缩小):

把两处 dpi 改成一致后恢复正常:

规则就一条:showtext_opts(dpi) = ggsave(dpi) = 你期望的输出分辨率。
坑 3:showtext 不支持 quartz 和 ragg 设备——"装了还乱码"的元凶
这是我踩到的最深的一个坑。同一个脚本,RStudio 里正常、命令行 Rscript 导出就乱码,原因在于设备不同:
- macOS 命令行 R 的 png() 默认走 quartz 后端(
getOption("bitmapType")返回 "quartz"),showtext 不支持 quartz; - ggplot2 3.3+ 的 ggsave 在装了 ragg 包时默认走 ragg 设备,showtext 也不支持 ragg;
- showtext 真正依赖的是 cairo 后端,而 macOS 的 cairo 依赖 X11——没装 XQuartz 的机器上会报
dlopen ... R_X11.so ... Library not loaded: /opt/X11/lib/...(我实测就报了这个)。
对应的解法:
- 最简单:在 RStudio 里出图(RStudio 的绘图设备支持 showtext);
- 服务器/命令行:安装 XQuartz 让 cairo 可用,或者干脆不装 showtext、改用 ragg 设备 + 系统字体名(方案 B 的思路);
- Linux 服务器:先装系统字体(如 Ubuntu 的
fonts-noto-cjk),再用方案 A。
三平台字体速查表
| 平台 | 系统字体名(方案 B) | 字体文件路径(方案 A 的 font_add) |
|---|---|---|
| Windows | Microsoft YaHei、SimHei | C:/Windows/Fonts/msyh.ttc、simhei.ttf |
| macOS | PingFang SC、Hiragino Sans GB | /System/Library/Fonts/Hiragino Sans GB.ttc(新版无 PingFang.ttc,见坑 1) |
| Linux | 需先安装 fonts-noto-cjk | /usr/share/fonts/opentype/noto/NotoSansCJK-Regular.ttc |
小结
中文乱码不是玄学,按这个顺序排查基本都能解决:① 跑诊断三行,确认设备类型 → ② RStudio / Windows / Linux 优先方案 A(showtext + font_add_google)→ ③ macOS 可以直接试方案 B(系统字体名)→ ④ 方案 A 无效时,检查是不是踩了坑 3 的设备问题。记住两个一致性:两处 dpi 要一致、font_add 的 family 名和 element_text 里写的一致。
相关文章:R 教程-11:简单绘图|R 教程-12:进阶绘图|ggplot2 散点图实操:图层原理、分组着色、形状、调色板与主题一次学会|R 教程系列目录(23 篇导航)
本文实测环境:R 4.3.1(macOS arm64)、ggplot2 3.5.2、showtext 0.9.7、sysfonts 0.8.9。不同环境报错信息可能略有差异,欢迎在评论区贴出你的诊断三行结果。