Java中文乱码&特殊字符解决方案

相信很多朋友遇到过Java的乱码问题,最近我也在解决一个“使用文本生成图片过程中中文以及特殊字符乱码”的问题;花了我大量时间,Debug了sun.font、sun.awt下面的各种源码,终于搞懂了其机制,解决了目前次问题;现在把问题解决过程给写下来,做个记录,以免以后再次遇到。

遇到的问题

下面是我想要执行的代码(经过极度简化,但是意思没变):

1 public static void main(String[] args) throwsIOException {2 File file = new File("test.png");3 Font font = new Font("宋体", Font.PLAIN, 10);4 BufferedImage bi = new BufferedImage(400, 200, BufferedImage.TYPE_INT_ARGB);5 Graphics2D g2 =(Graphics2D) bi.getGraphics();6 g2.setBackground(Color.WHITE);7 g2.clearRect(0, 0, 400, 200);8 g2.setFont(font);9 g2.setColor(Color.BLACK);10 g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);11 g2.drawString("为什么没有(????)(????)这名字特殊不?@¥¥¥ 为什么没有(????)(????)这名字特 ", 0, 10);12 g2.dispose();13 ImageIO.write(bi, PNG, file);14 }

目标当然是想在打开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自带字体:

1 # @(#)linux.fontconfig.SuSE.properties 1.2 03/10/17

2 #3 # Copyright 2003Sun Microsystems, Inc. All rights reserved.4 #5

6 # Version7 version=1

8

9 # Component Font Mappings10 serif.plain.chinese=-misc-simsun-medium-r-normal--*-%d-*-*-c-*-iso10646-1

11 serif.plain.latin-1=-b&h-lucidabright-medium-r-normal--*-%d-*-*-p-*-iso8859-1

12

13 # Search Sequences14 sequence.allfonts=latin-1,chinese15

16 # Exclusion Ranges17

18 # Font File Names19 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 特殊字符 乱码_Java中文乱码特殊字符解决方案相关推荐

  1. java 空格乱码_java 中文乱码以及空格乱码解决方案总结

    当使用response.write返回JSON数据到前端时,需要在前面加上这句 response.ContentType = "text/html;charset=gb2312"; ...

  2. java 页面编码_java中文乱码解决之道(七)-----JSP页面编码过程

    我们知道JSP页面是需要转换为servlet的,在转换过程中肯定是要进行编码的.在JSP转换为servlet过程中下面一段代码起到至关重要的作用. 在上面代码中有两个地方存在编码:pageEncodi ...

  3. java encode乱码_java 中文乱码问题的解决

    java中的中文乱码问题应该是我们经常碰到的一个问题,今天就来总结下对于乱码问题的解决方法: 1. 超链接中带有的中文字符,添加 这样如果不进行处理在后台得到的数据就会出现中文乱码的问题,由于超链接实 ...

  4. c与java的中文乱码_Java 中文乱码问题总结

    由于Java编程中的中文问题是一个老生常谈的问题,在阅读了许多关于Java中文问 题解决方法之后,结合作者的编程实践,我发现过去谈的许多方法都不能清晰地说明问题及解决问题,尤其是跨平台时的中文问题.于 ...

  5. java转换中文乱码_java中文乱码解决之道(四)-----java编码转换过程

    前面三篇博客侧重介绍字符.编码问题,经过这三篇博客各位博友对各类字符编码有了一个初步的了解,要了解java的中文问题这是必需要了解的.可是了解这些仅仅只是一个开始,如下博客将侧重介绍java乱码是如何 ...

  6. linux下java程序乱码,解决linux下java程序(例如applet)中文乱码问题

    解决linux下java程序(例如applet)中文乱码问题 平台:Mandriva 2006.0 将Windows下的字体文件simsun.ttc拷贝到/usr/share/fonts/zh_CN, ...

  7. java解决properties中中文乱码问题

    Java解决properties中中文乱码的问题 Properties文件介绍 与Properties文件的前世今生 native2ascii.exe的使用 首先编写配置文件 实验 修改测试代码进行更 ...

  8. java 插入 mysql 乱码_解决java中插入mysql中文乱码的方法

    解决java中插入mysql中文乱码的方法 发布时间:2020-07-11 14:35:11 来源:亿速云 阅读:100 作者:清晨 这篇文章主要介绍解决java中插入mysql中文乱码的方法,文中介 ...

  9. mysql导出excel出乱码_Mysql中文乱码以及导出为sql语句和Excel问题解决方法[图文]...

    一.导出数据. 先说明一下自己的环境:Mac OS X 10.8.3, MySQL Community Server 5.6.10, MySQL Workbench 5.2.47. 我想把本机数据库内 ...

最新文章

  1. junit配合catubuter统计单元测试的代码覆盖率
  2. 向textarea元素输入限制长度的字符
  3. Angularjs Controller 间通信机制
  4. Android 生成随机数,获取一条随机字符串
  5. git使用—rebase还是merge
  6. ios 逆向编程(环境搭建)
  7. 记一次ZABBIX监控JMX故障
  8. 《哈里波特:魔法觉醒》被指侮辱女性玩家?官方回应:动画BUG
  9. visual studio 资源编辑器 标尺和参考线,网格线
  10. 华为手机解锁码快速申请方式
  11. QTcpSocket的使用步骤
  12. 设定Applocker和解决问题
  13. 解决data too long for column 'name' at row2
  14. JAVA 处理带AM 或 PM的时间
  15. 前端基础学习——javaScript正则表达式详解
  16. 数字IC设计工程师要具备哪些技能
  17. ListView动态刷新某项Item
  18. PDF如何转换成PPT?教你们几个简单方法
  19. 编写代码实现:模拟用户登入情景
  20. uC/OS-II+ucGUI 3.24 保护模式下的移植

热门文章

  1. COleVariant在多字节字符集下的坑
  2. 如何用VGA接口乳法?
  3. 基于CubeMX-STM32F302_uCOSII例程移植
  4. Embedding 编码方法
  5. 推荐系统中常用的embedding方法
  6. matlab三维显示圆锥,matlab画圆锥曲面
  7. IPv6协议的技术分析
  8. 用计算机计算一定比笔算简便,不论是口算还是笔算,是估算还是用计算器算,其基本算理.doc...
  9. Linux常见命令 12 - ps, kill, pkill, w, tty
  10. 【数电试题】西电通卓模拟卷三