首頁 > 運維 > linux運維 > 主體

如何解決java linux檔中文亂碼問題

藏色散人
發布: 2021-12-14 14:35:05
原創
5686 人瀏覽過

java linux檔案中文亂碼的解決方法:1、下載jdk1.8的sun原始碼;2、將Font的創建從實體字體改為邏輯字體;3、重啟服務即可。

如何解決java linux檔中文亂碼問題

本文操作環境:linux5.9.8系統,jdk1.8,Dell G3電腦。

如何解決java linux檔案中文亂碼問題?

#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权限

推荐学习:《linux视频教程

以上是如何解決java linux檔中文亂碼問題的詳細內容。更多資訊請關注PHP中文網其他相關文章!

相關標籤:
來源:php.cn
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板