共计 4480 个字符,预计需要花费 12 分钟才能阅读完成。
这篇文章给大家分享的是有关 java linux 文件出现中文乱码怎么办的内容。丸趣 TV 小编觉得挺实用的,因此分享给大家做个参考,一起跟随丸趣 TV 小编过来看看吧。
java linux 文件中文乱码的解决办法:1、下载 jdk1.8 的 sun 源码;2、将 Font 的创建从物理字体改为逻辑字体;3、重启服务即可。
本文操作环境:linux5.9.8 系统,jdk1.8,Dell G3 电脑。
Linux 环境下 Java 中文乱码解决方案
相信很多朋友遇到过 Java 的乱码问题,最近我也在解决一个“使用文本生成图片过程中中文以及特殊字符乱码”的问题;花了我大量时间,Debug 了 sun.font、sun.awt 下面的各种源码,终于搞懂了其机制,解决了目前次问题;现在把问题解决过程给写下来,做个记录,以免以后再次遇到。
遇到的问题
下面是我想要执行的代码(经过极度简化,但是意思没变):
public static void main(String[] args) throws IOException {
File file = new File( test.png
Font font = new Font(宋体 , Font.PLAIN, 10);
BufferedImage bi = new BufferedImage(400, 200, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2 = (Graphics2D) bi.getGraphics();
g2.setBackground(Color.WHITE);
g2.clearRect(0, 0, 400, 200);
g2.setFont(font);
g2.setColor(Color.BLACK);
g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
g2.drawString(为什么没有 (ꐚꌒꑿꆺ)(ꐚꌒꑿꆺ) 这名字特殊不?@¥¥¥ 为什么没有 (ꐚꌒꑿꆺ)(ꐚꌒꑿꆺ) 这名字特 , 0, 10);
g2.dispose();
ImageIO.write(bi, PNG, file);
}
目标当然是想在打开 test.png 的时候看到如下场景:
在本地调试没问题之后,就放到了测试机(Linux)上面去执行了,执行结果简直扑街:
jdk1.8 的 sun 源码下载
奉行程序员一贯作风:既然有问题,那就 Debug!
坑爹的是现在的源码包已经不包含 sun 包的代码了!
幸好 java 官方确认 OpenJDK 的代码基本和 JVM 源码一致,可以直接从 OpenJDK8u 进行下载:jdk8u
至于如何使用源码 debug,这个就不写了··· 这都不会基本也就别看这文章了
定位问题
直接下载好源码,远程断点,服务器执行,在 debug 中先发现了第一个产生本地和测试服务器不一致的代码:
原来 JVM 创建 Font 的时候会使用 FontManagerFactory 获取 FontManager,而不同的系统使用的 FontManager 是不同的!Mac 用的是 CFontManager,而 Linux 用的是 X11FontManager!
那么这两个 FontManager 的不同会导致什么不同呢?
CFontManager 会创建 CFont 作为 Font2D,这个 CFont 是 JVM 专门为 mac 创建的类,看类和方法的注释可以知道在 mac 环境下有时候物理字体会被 CFont 包装,而这是在 native 代码中完成的:
X11FontManager 创建的 Font2D 是包含了逻辑字体和物理字体的集合。X11FontManager 继承了 FcFontManager,FcFontManager 继承了 SunFontManager;我们看一下 X11FontManager 的 loadFonts()方法,直接使用了 SunFontManager 的 loadFonts(),SunFontManager 的 loadFonts()方法加载了物理字体,SunFontManager 实现了 FontManager 的 preferLocaleFonts()方法,加载了逻辑字体:
逻辑字体与物理字体
代码 debug 到这边基本已经确认了是不同环境的字体加载问题,那么在 debug linux 环境的时候发现的逻辑字体和物理字体是什么东西呢?
物理字体
物理字体是实际的字体库,包含字形数据和表,这些数据和表使用字体技术(如 TrueType 或 PostScript Type 1)将字符序列映射到字形序列。Java Platform 的所有实现都支持 TrueType 字体;对其他字体技术的支持是与实现相关的。物理字体可以使用字体名称,如 Helvetica、Palatino、HonMincho 或任意数量的其他字体名称。通常,每种物理字体只支持有限的书写系统集合,例如,只支持拉丁文字符,或者只支持日文和基本拉丁文。可用的物理字体集合随配置的不同而有所不同。要求特定字体的应用程序可以使用 createFont 方法来捆绑这些字体,并对其进行实例化。
逻辑字体
逻辑字体是由必须受所有 Java 运行时环境支持的 Java 平台所定义的五种字体系列:Serif、SansSerif、Monospaced、Dialog 和 DialogInput。这些逻辑字体不是实际的字体库。此外,由 Java 运行时环境将逻辑字体名称映射到物理字体。映射关系与实现和通常语言环境相关,因此它们提供的外观和规格各不相同。通常,为了覆盖庞大的字符范围,每种逻辑字体名称都映射到几种物理字体。
问题解决
debug 的源码很多,但是此次问题的关键点就在这里了,其它 debug 内容就不贴了。
既然已经确认了本地(mac 环境)是 native 的代码帮我们做了物理字体的封装,转换成了 CFont 进行渲染,而 Linux 环境的 X11FontManager 只是帮我们加载了物理字体和逻辑字体,但是却需要我们自己进行选择,那么解决问题的第一步就显而易见了:将 Font 的创建从物理字体改为逻辑字体
1 // Serif、SansSerif、Monospaced、Dialog 和 DialogInput 随意选择
2 Font font = new Font(Serif , Font.PLAIN, 10);
改完以后执行代码,仍然是乱码!继续 Debug,发现是 Linux 上逻辑字体 Serif 映射的物理字体没有中文字体和对应的特殊符号字体,这就很简单了,直接在 Linux 上安装中文字体(simsun.ttf),再安装特殊符号“ꐚꌒꑿꆺ”可显示的字体(mysi.ttf),将这两个字体也放到了 jdk 的 fonts 目录(JAVA_HOME/jre/lib/fonts)下。文章后面有 Linux 字体安装方法。
完成上面的改动之后,重启服务,再次执行成功显示!热烈庆祝~~~~
JVM 逻辑字体映射配置
以上的改动已经可以解决中文和特殊字符乱码问题,但是我在 Debug 过程中发现在逻辑字体加载过程中,JVM 会参考一个配置文件,代码在 sun.awt.FontConfiguration 中,这个配置类完成了逻辑字体和物理字体的映射,也指导了 SunFontManager 创建逻辑字体,而这个 FontConfiguration 读取的配置文件就是 fontconfig.properties,这个配置文件目录是 JAVA_HOME/jre/lib
查阅了一下资料,JVM 字体配置文件的加载顺序如下:
JAVA_HOME/jre/lib/fontconfig.OS.Version.properties
JAVA_HOME/jre/lib/fontconfig.OS.Version.bfc
JAVA_HOME/jre/lib/fontconfig.OS.properties
JAVA_HOME/jre/lib/fontconfig.OS.bfc
JAVA_HOME/jre/lib/fontconfig.Version.properties
JAVA_HOME/jre/lib/fontconfig.Version.bfc
JAVA_HOME/jre/lib/fontconfig.properties
JAVA_HOME/jre/lib/fontconfig.bfc
OS 是系统,例如:Linux、CentOs、RedHat 等;Version 是版本号
在这个配置文件中可以修改逻辑字体与物理字体的对应关系,也就是说可以手动的修改 Serif、SansSerif、Monospaced、Dialog 和 DialogInput 这五个逻辑字体在不同场景下所使用的真正物理字体。
举个栗子,下面的配置将 serif.plain 逻辑字体的中文使用 simsun.ttf,拉丁文使用 java 自带字体:
# @(#)linux.fontconfig.SuSE.properties 1.2 03/10/17
# Copyright 2003 Sun Microsystems, Inc. All rights reserved.
# Version
version=1
# Component Font Mappings
serif.plain.chinese=-misc-simsun-medium-r-normal--*-%d-*-*-c-*-iso10646-1
serif.plain.latin-1=-b h-lucidabright-medium-r-normal--*-%d-*-*-p-*-iso8859-1
# Search Sequences
sequence.allfonts=latin-1,chinese
# Exclusion Ranges
# Font File Names
filename.-misc-simsun-medium-r-normal--*-%d-*-*-c-*-iso10646-1=/usr/share/fonts/myfonts/simsun.ttf
Linux 安装字体
Linux 字体目录:/usr/share/fonts
在 fonts 下面新建一个目录,例如:mkdir myfonts
将需要安装的字体放到新建目录下面,例如:cp ~/test/simsun.ttf /usr/share/fonts/myfonts
进入到 myfonts 目录:cd /usr/share/fonts/myfonts
执行如下命令:
mkfontscale
mkfontdir
fc-cache -fv
查看是否已经安装对应的字体:fc-list
fc-cache -fv 命令用来刷新 linux 的字体缓存,使其立刻生效
PS:以上所有操作基本都需要 root 权限
感谢各位的阅读!关于“java linux 文件出现中文乱码怎么办”这篇文章就分享到这里了,希望以上内容可以对大家有一定的帮助,让大家可以学到更多知识,如果觉得文章不错,可以把它分享出去让更多的人看到吧!