java linux檔案中文亂碼的解決方法:1、下載jdk1.8的sun原始碼;2、將Font的創建從實體字體改為邏輯字體;3、重啟服務即可。
本文操作環境: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)上面去執行了,執行結果簡直撲街:
奉行程式設計師一貫作風:既然有問題,那就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字体安装方法。
完成上面的改动之后,重启服务,再次执行成功显示!热烈庆祝~~~~
以上的改动已经可以解决中文和特殊字符乱码问题,但是我在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
PS:以上所有操作基本都需要root权限
推荐学习:《linux视频教程》
以上是如何解決java linux檔中文亂碼問題的詳細內容。更多資訊請關注PHP中文網其他相關文章!