ubuntu系统下matplotlib中文乱码问题的解决方法
Page content
ubuntu系统下matplotlib中文乱码问题的解决方法。
1、将字体文件放在matplotlib的字体目录下
查看matplotlib配置文件位置:
import matplotlib
print(matplotlib.matplotlib_fname())
/root/anaconda3/lib/python3.8/site-packages/matplotlib/mpl-data/matplotlibrc
则将字体文件放在/root/anaconda3/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/ttf目录下。
2、修改matplotlib配置文件:
去掉3行注释,并添加字体配置:
font.family : sans-serif
font.sans-serif : SimHei, Bitstream Vera Sans, Lucida Grande, Verdana, Geneva, Lucid, Arial, Helvetica, Avant Garde, sans-serif
axes.unicode_minus : False #解决负号'-'显示为方块的问题
3、删除当前用户matplotlib 的缓存文件:
cd ~/.cache/matplotlib
rm -rf *.*
4、一键复制字体文件到matplotlib的字体目录下的脚本:
import os
import shutil
import matplotlib
import matplotlib.pyplot as plt
import matplotlib.font_manager as fm
from matplotlib.textpath import TextPath
# 默认推荐的中文字体关键词,用于匹配系统已安装字体
default_font_keywords = ['SimHei', 'YaHei', 'FangSong', 'Heiti', 'Microsoft YaHei', 'Noto Sans CJK']
# 获取 Matplotlib 的字体目录、缓存目录和配置文件路径
matplotlib_dir = os.path.dirname(matplotlib.matplotlib_fname())
matplotlib_font_dir = os.path.join(matplotlib_dir, 'fonts', 'ttf') # 字体文件目录
matplotlib_cache_dir = matplotlib.get_cachedir() # 缓存目录,自动选择合适目录
matplotlibrc_path = matplotlib.matplotlib_fname() # matplotlibrc 配置文件路径
def supports_chinese(font_path, test_char='汉'):
"""
判断指定字体文件是否支持中文
:param font_path: 字体文件路径
:param test_char: 用于测试的中文字符,默认为 "汉"
:return: True 支持,False 不支持
"""
try:
font_prop = fm.FontProperties(fname=font_path) # 创建字体属性对象
text_path = TextPath((0, 0), test_char, prop=font_prop) # 通过 TextPath 演练
return text_path.vertices.size > 0 # 如果有顶点,则表明能演练
except Exception:
return False # 如果异常,则不支持
def find_chinese_fonts():
"""
找出系统支持中文的字体
:return: 返回 {font_name: font_path} 字典
"""
font_paths = fm.findSystemFonts(fontpaths=None, fontext='ttf') # 搜索 TTF 字体文件
font_dict = {}
for path in font_paths:
if supports_chinese(path): # 如果支持中文
try:
font_name = fm.FontProperties(fname=path).get_name() # 获取字体名
if font_name not in font_dict:
font_dict[font_name] = path # 保存字体名和路径
except Exception:
continue # 如果获取失败,继续
return font_dict
def copy_fonts_to_matplotlib(font_dict, keywords):
"""
将系统中符合关键词的字体备份到 Matplotlib 字体目录
:return: [(font_name, font_path), ...]
"""
copied_fonts = []
for font_name, font_path in font_dict.items():
if any(k.lower() in font_name.lower() for k in keywords): # 检查是否包含推荐关键词
try:
target_path = os.path.join(matplotlib_font_dir, os.path.basename(font_path))
if not os.path.exists(target_path): # 如果未存在,则备份
shutil.copy(font_path, target_path)
print(f"✅ 已拷贝字体: {font_name}")
else:
print(f"🟡 已存在字体: {font_name},跳过")
copied_fonts.append((font_name, font_path))
except Exception as e:
print(f"❌ 拷贝失败 {font_name}: {e}")
return copied_fonts
def clear_matplotlib_cache():
"""
清除 Matplotlib 缓存文件,确保新字体生效
"""
if os.path.exists(matplotlib_cache_dir):
for f in os.listdir(matplotlib_cache_dir):
file_path = os.path.join(matplotlib_cache_dir, f)
try:
if os.path.isfile(file_path):
os.remove(file_path)
except Exception:
continue
def auto_update_matplotlibrc(font_names):
"""
自动更新 matplotlibrc 配置文件,设置默认字体和设置负号显示
:param font_names: 列表,包含要设置为默认无衬线字体的字体名称
"""
# 检查 matplotlibrc 配置文件是否存在
if not os.path.exists(matplotlibrc_path):
print(f"❌ 找不到配置文件: {matplotlibrc_path}")
return
# 以只读模式打开配置文件,将文件内容按行读取到列表中
with open(matplotlibrc_path, 'r', encoding='utf-8') as f:
lines = f.readlines()
# 用于存储更新后的配置文件行
updated_lines = []
# 标记是否已经更新了 font.family、font.sans-serif 和 axes.unicode_minus 配置
has_family, has_sans, has_minus = False, False, False
# 生成新的 font.sans-serif 配置行,使用传入的字体名称列表
new_sans_line = f"font.sans-serif: {', '.join(font_names)}\n"
new_family_line = "font.family: sans-serif\n"
new_minus_line = "axes.unicode_minus: False\n"
# 遍历配置文件的每一行
for line in lines:
# 去除当前行首尾的空白字符
stripped = line.strip()
if stripped == new_family_line.strip():
updated_lines.append(new_family_line)
has_family = True
elif stripped == new_sans_line.strip():
updated_lines.append(new_sans_line)
has_sans = True
elif stripped == new_minus_line.strip():
updated_lines.append(new_minus_line)
has_minus = True
elif stripped.startswith('#') and 'font.family' in stripped:
if not has_family:
updated_lines.append(new_family_line)
has_family = True
else:
updated_lines.append(line)
elif stripped.startswith('font.family'):
if not has_family:
updated_lines.append(new_family_line)
has_family = True
else:
updated_lines.append(line)
elif stripped.startswith('#') and 'font.sans-serif' in stripped:
if not has_sans:
updated_lines.append(new_sans_line)
has_sans = True
else:
updated_lines.append(line)
elif stripped.startswith('font.sans-serif'):
if not has_sans:
updated_lines.append(new_sans_line)
has_sans = True
else:
updated_lines.append(line)
elif stripped.startswith('axes.unicode_minus'):
if not has_minus:
updated_lines.append(new_minus_line)
has_minus = True
else:
updated_lines.append(line)
elif stripped.startswith('#') and 'axes.unicode_minus' in stripped:
if not has_minus:
updated_lines.append(new_minus_line)
has_minus = True
else:
updated_lines.append(line)
else:
updated_lines.append(line)
# 如果未更新 font.family 配置,在更新后的行列表末尾添加新的 font.family 配置
if not has_family:
updated_lines.append(new_family_line)
# 如果未更新 font.sans-serif 配置,在更新后的行列表末尾添加新的 font.sans-serif 配置
if not has_sans:
updated_lines.append(new_sans_line)
# 如果未更新 axes.unicode_minus 配置,在更新后的行列表末尾添加新的 axes.unicode_minus 配置
if not has_minus:
updated_lines.append(new_minus_line)
# 以写入模式打开配置文件,将更新后的内容写入
with open(matplotlibrc_path, 'w', encoding='utf-8') as f:
f.writelines(updated_lines)
# 打印配置文件更新成功信息
print(f"\n✅ 配置文件已更新: {matplotlibrc_path}")
print(" - font.family : sans-serif")
print(f" - font.sans-serif : {', '.join(font_names)}")
print(" - axes.unicode_minus : False")
def draw_test_plot():
"""
绘制一个测试图表,确保中文字体生效
"""
plt.figure()
plt.plot([1, 2, 3], [1, 4, 9], label="示例曲线")
plt.title("中文标题测试")
plt.xlabel("横坐标")
plt.ylabel("纵坐标")
plt.legend()
plt.show()
print("\n📈 已保存测试图像 test_plot.png")
def main():
print("🔍 正在查找系统中支持中文的字体...\n")
font_dict = find_chinese_fonts()
font_list = sorted(font_dict.keys(), key=lambda x: x.lower())
print("✅ 推荐字体中系统已安装的有:\n")
available_fonts = []
for font_name in font_list:
if any(k.lower() in font_name.lower() for k in default_font_keywords):
available_fonts.append(font_name)
print(" -", font_name)
print("\n📦 正在拷贝字体到 Matplotlib 字体目录...\n")
copied_fonts = copy_fonts_to_matplotlib(font_dict, default_font_keywords)
if copied_fonts:
copied_font_names = [name for name, _ in copied_fonts]
clear_matplotlib_cache() # 清除缓存
auto_update_matplotlibrc(copied_font_names) # 配置 matplotlibrc
draw_test_plot() # 生成测试图
else:
print("⚠️ 未找到推荐字体,未备份或修改配置。")
print("\n✅ 完成!你现在可以在 Matplotlib 中使用正确的中文字体了。")
if __name__ == '__main__':
main()