原創鏈接:https://blog.csdn.net/lordofadventure/article/details/104281135
目錄
一、命令行亂碼與chcp指令
cmd命令行中有時會發生亂碼的情況,這是由於需要顯示的字符不在當前代碼頁的字符集中,在cmd命令行中使用chcp命令可以顯示當前代碼頁。【1】
編號 |
含義 |
---|---|
936 | 簡體中文(GBK編碼) |
65001 | Unicode(UTF-8) |
437 | 美國英語(DOS默認) |
可以在【3】和【4】上查詢UTF-8的相關字符信息
可以使用如下bat腳本驗證一個包含UTF-8字符的文件
chcp 65001 %將活動代碼頁設置爲UTF-8,僅一次性有效%
type test.java %顯示文件內容,相當於shell命令中的cat%
javac -encoding UTF-8 test.java
java test
echo ã %打印字符%
echo 你好
pause
二、UTF-8與BOM頭
當文件中包含漢字時,直接編譯可能會報錯,這時候需要在javac命令中加上encoding選項
javac -encoding UTF-8 test.java
但是此時會報如下錯誤
這個非法字符其實就是記事本的UTF-8格式中在文件最前面所加的BOM頭【6】,而之所以會報錯則是因爲java內部的UTF-8編碼機制中不會對BOM頭進行識別,長期以來一直有人建議java改進這一問題,但是Oracle在官網上解釋並聲明瞭java現在及以後都不會添加識別BOM頭的功能,所有的應用必須自己完成BOM頭的識別【7】。一種解決方案是將文本文件再另外保存爲Unicode格式並且在編譯時也使用Unicode選項,但是這麼做依然無法解決部分UTF-8特有文字無法顯示的問題,另外一種解決方案就是更換文本文件,使用一種對於格式支持更加強大的軟件,比如說notepad++【8】,它支持用戶選擇是否在UTF-8格式中保留BOM頭,只要將文件使用notepad++轉換爲不包含BOM頭的格式,再使用javac -encoding UTF-8編譯就可以了,notepad++可以在其官網下載【5】。
三、java編碼方式與活動代碼頁的編碼衝突
在使用notepad++去除BOM頭並且修改活動代碼頁爲65001的情況下,一部分java輸出依然無法正確顯示。
如下test.java程序
public class test{
public static void main(String args[]){
System.out.println("你好");
System.out.println("ã");
}
}
【情形一】執行如下命令(採用默認的活動代碼頁936,javac編譯採用默認編碼方式)
type test.java
javac test.java
java test
echo 你好
echo ã
輸出情況爲:type打印的test.java中的特殊字符亂碼,能夠編譯但是輸出亂碼,echo命令不能正確打印特殊字符
【情形二】執行如下命令(設置活動代碼頁65001,javac 採用默認編碼方式)
chcp 65001
type test.java
javac test.java
java test
echo 你好
echo ã
輸出情況爲:全部正確輸出
【情形三】執行如下命令(採用默認代碼頁936,javac採用UTF-8編碼選項)
type test.java
javac -encoding UTF-8 test.java
java test
echo 你好
echo ã
輸出情況爲:type打印的test.java中的特殊字符亂碼,能夠編譯,輸出的字符中,漢字正確顯示,ã字符亂碼,echo命令不能正確打印特殊字符
【情形四】執行如下命令(設置活動代碼頁65001,javac採用UTF-8編碼選項)
chcp 65001
type test.java
javac -encoding UTF-8 test.java
java test
echo 你好
echo ã
輸出情況爲:type正確打印,能夠編譯但是無法正確輸出,echo正確打印
彙總四種情形如下:
活動代碼頁 | -encoding 選項 | type結果 | echo結果 | javac編譯 | java輸出 |
---|---|---|---|---|---|
936 | / | × | × | √ | × |
936 | UTF-8 | × | × | √ | ×(漢字√) |
65001 | / | √ | √ | √ | √ |
65001 | UTF-8 | √ | √ | √ | × |
通過如下語句可以查看java默認的編碼方式【9】:
System.out.println(System.getProperty("sun.jnu.encoding"));
我顯示的默認編碼方式爲GBK,所以對於上述表格現象的解釋爲:type和echo是否能夠正確輸出取決於當前活動代碼頁的字符集是否包含需要打印的字符,因此當設置爲65001也就是UTF-8時,type和echo都能夠正確顯示結果;比較奇怪的是,在活動代碼頁設置爲65001時只有採用默認編碼方式也就是GBK編碼時java才能正確輸出,而如果設置encoding選項爲UTF-8則反而不能正確輸出了。
今天又出現了一個新情況,那就是如果使用java文件新建窗口可視化輸出的時候,如果設置不設置encoding選項,則窗口中的輸出字符亂碼但是命令行輸出正確,如果設置encoding選項,則窗口中的輸出正確但是命令行無法正確輸出,目前沒有搞清楚原因,推測可能是由於新建窗口中的默認編碼方式和普通字符串的默認編碼方式又有所不同。
四、後續補充
如果要修改JDK默認編碼方式的話,可以參考官網文檔【10】,或是這篇文檔【11】
另外還有一些字符串編碼變換的操作【12】,涉及到Charset集【13】:
String str = new String(strs.getBytes(), "utf-8");
不過我在進行如上操作時報了UnsupportedEncodingException錯誤【14】,還沒有徹底搞清楚原因【15】。
另有關BOM頭操作的補充材料見【16】。
參考文獻
【1】Win10 修改cmd命令行窗口UTF-8編碼
https://blog.csdn.net/tfs411082561/article/details/78416569
【2】代碼頁
https://baike.baidu.com/item/%E4%BB%A3%E7%A0%81%E9%A1%B5/11025504?fr=aladdin#6_1
【3】http://www.mytju.com/classcode/tools/encode_utf8.asp
【4】https://www.utf8-chartable.de/
【5】https://notepad-plus-plus.org/
【6】常見編碼和編碼頭BOM
【7】JDK-4508058 : UTF-8 encoding does not recognize initial BOM
https://bugs.java.com/bugdatabase/view_bug.do?bug_id=4508058
【8】How to compile a java source file which is encoded as “UTF-8”?
【9】java查詢當前操作系統的默認編碼方式
https://blog.csdn.net/liangwenmail/article/details/78604731
【10】https://docs.oracle.com/javase/6/docs/platform/jvmti/jvmti.html#tooloptions
【11】將JDK默認編碼設置爲UTF-8
【12】System.out.println() 輸出中文亂碼
https://blog.csdn.net/pkuyjxu/article/details/7493801
【13】https://docs.oracle.com/javase/8/docs/api/index.html
【14】https://docs.oracle.com/javase/8/docs/api/java/io/UnsupportedEncodingException.html
【15】Java8 FileSystems.getDefault throw UnsupportedCharsetException
【16】How to use UTF-8, UTF-8 with BOM marker, XML and Java iostreams together