簡介
小師妹一時興起,使用了一項從來都沒用過的新技能,沒想卻出現了一個無法解決的問題。把大象裝進冰箱到底有幾步?亂碼的問題又是怎么解決的?快來跟F師兄一起看看吧。
更多精彩內容且看:
- 區塊鏈從入門到放棄系列教程-涵蓋密碼學,超級賬本,以太坊,Libra,比特幣等持續更新
- Spring Boot 2.X系列教程:七天從無到有掌握Spring Boot-持續更新
- Spring 5.X系列教程:滿足你對Spring5的一切想象-持續更新
- java程序員從小工到專家成神之路(2020版)-持續更新中,附詳細文章教程
使用Properties讀取文件
這天,小師妹心情很愉悅,吹着口哨唱着歌,標准的45度俯視讓人好不自在。
小師妹呀,什么事情這么高興,說出來讓師兄也沾點喜慶?
小師妹:F師兄,最新我發現了一種新型的讀取文件的方法,很好用的,就跟map一樣:
public void usePropertiesFile() throws IOException {
Properties configProp = new Properties();
InputStream in = this.getClass().getClassLoader().getResourceAsStream("www.flydean.com.properties");
configProp.load(in);
log.info(configProp.getProperty("name"));
configProp.setProperty("name", "www.flydean.com");
log.info(configProp.getProperty("name"));
}
F師兄你看,我使用了Properties來讀取文件,文件里面的內容是key=value形式的,在做配置文件使用的時候非常恰當。我是從Spring項目中的properties配置文件中得到的靈感,才發現原來java還有一個專門讀取屬性文件的類Properties。
小師妹現在都會搶答了,果然青出於藍。
亂碼初現
小師妹你做得非常好,就這樣觸類旁通,很快java就要盡歸你手了,后面的什么scala,go,JS等估計也統統不在話下。再過幾年你就可以升任架構師,公司技術在你的帶領之下一定會蒸蒸日上。
做為師兄,最大的責任就是給小師妹以鼓勵和信心,給她描繪美好的未來,什么出任CEO,贏取高富帥等全都不在話下。聽說有個專業的詞匯來描述這個過程叫做:畫餅。
小師妹有點心虛:可是F師兄,我還有點小小的問題沒有解決,有點中文的小小亂碼....
我深有體會的點點頭:馬賽克是阻礙人類進步的絆腳石...哦,不是馬賽克,是文件亂碼,要想弄清楚這個問題,還要從那個字符集和文件編碼講起。
字符集和文件編碼
在很久很久以前,師兄我都還沒有出生的時候,西方世界出現了一種叫做計算機的高科技產品。
初代計算機只能做些簡單的算數運算,還要使用人工打孔的程序才能運行,不過隨着時間的推移,計算機的體積越來越小,計算能力越來越強,打孔已經不存在了,編程了人工編寫的計算機語言。
一切都在變化,唯有一件事情沒有變化。這件事件就是計算機和編程語言只流傳在西方。而西方日常交流使用26個字母加有限的標點符號就夠了。
最初的計算機存儲可以是非常昂貴的,我們用一個字節也就是8bit來存儲所有能夠用到的字符,除了最開始的1bit不用以外,總共有128中選擇,裝26個小寫+26個大寫字母和其他的一些標點符號之類的完全夠用了。
這就是最初的ASCII編碼,也叫做美國信息交換標准代碼(American Standard Code for Information Interchange)。
后面計算機傳到了全球,人們才發現好像之前的ASCII編碼不夠用了,比如中文中常用的漢字就有4千多個,怎么辦呢?
沒關系,將ASCII編碼本地化,叫做ANSI編碼。1個字節不夠用就用2個字節嘛,路是人走出來的,編碼也是為人來服務的。於是產生了各種如GB2312, BIG5, JIS等各自的編碼標准。這些編碼雖然與ASCII編碼但是相互之間缺並不兼容。
這嚴重的影響了國際化的進程,這樣還怎么去實現同一個地球,同一片家園的夢想?
於是國際組織出手了,制定了UNICODE字符集,為所有語言的所有字符都定義了一個唯一的編碼,unicode的字符集是從U+0000到U+10FFFF這么多個編碼。
小師妹:F師兄,那么unicode和我平時聽說的UTF-8,UTF-16,UTF-32有什么關系呢?
我笑着問小師妹:小師妹,把大象裝進冰箱有幾步?
小師妹:F師兄,腦筋急轉彎的故事,已經不適合我了,大象裝進冰箱有三步,第一打開冰箱,第二把大象裝進去,第三關上冰箱,完事了。
小師妹呀,作為一個有文化的中國人,要真正的承擔起民族復興,科技進步的大任,你的想法是很錯誤的,不能光想口號,要有實際的可操作性的方案才行,要不然我們什么時候才能夠打造秦芯,唐芯和明芯呢?
師兄說的對,可是這跟unicode有什么關系呢?
unicode字符集最后是要存儲到文件或者內存里面的,那怎么存呢?使用固定的1個字節,2個字節還是用邊長的字節呢?根據編碼方式的不同,可以分為UTF-8,UTF-16,UTF-32等多種編碼方式。
其中UTF-8是一種變長的編碼方案,它使用1-6個字節來存儲。UTF-16使用2個或者4個字節來存儲,JDK9之后的String的底層編碼方式變成了兩種:LATIN1和UTF16。
而UTF-32是使用4個字節來存儲。這三種編碼方式中,只有UTF-8是兼容ASCII的,這也是為什么國際上UTF-8編碼方式比較通用的原因(畢竟計算機技術都是西方人搞出來的)。
解決Properties中的亂碼
小師妹,要解決你Properties中的亂碼問題很簡單,Reader基本上都有一個Charsets的參數,通過這個參數可以傳入要讀取的編碼方式,我們把UTF-8傳進去就行了:
public void usePropertiesWithUTF8() throws IOException{
Properties configProp = new Properties();
InputStream in = this.getClass().getClassLoader().getResourceAsStream("www.flydean.com.properties");
InputStreamReader inputStreamReader= new InputStreamReader(in, StandardCharsets.UTF_8);
configProp.load(inputStreamReader);
log.info(configProp.getProperty("name"));
configProp.setProperty("name", "www.flydean.com");
log.info(configProp.getProperty("name"));
}
上面的代碼中,我們使用InputStreamReader封裝了InputStream,最終解決了中文亂碼的問題。
真.終極解決辦法
小師妹又有問題了:F師兄,這樣做是因為我們知道文件的編碼方式是UTF-8,如果不知道該怎么辦呢?是選UTF-8,UTF-16還是UTF-32呢?
小師妹問的問題越來越刁鑽了,還好這個問題我也有准備。
接下來介紹我們的終極解決辦法,我們將各種編碼的字符最后都轉換成unicode字符集存到properties文件中,再讀取的時候是不是就沒有編碼的問題了?
轉換需要用到JDK自帶的工具:
native2ascii -encoding utf-8 file/src/main/resources/www.flydean.com.properties.utf8 file/src/main/resources/www.flydean.com.properties.cn
上面的命令將utf-8的編碼轉成了unicode。
轉換前:
site=www.flydean.com
name=程序那些事
轉換后:
site=www.flydean.com
name=\u7a0b\u5e8f\u90a3\u4e9b\u4e8b
再運行下測試代碼:
public void usePropertiesFileWithTransfer() throws IOException {
Properties configProp = new Properties();
InputStream in = this.getClass().getClassLoader().getResourceAsStream("www.flydean.com.properties.cn");
configProp.load(in);
log.info(configProp.getProperty("name"));
configProp.setProperty("name", "www.flydean.com");
log.info(configProp.getProperty("name"));
}
輸出正確的結果。
如果要做國際化支持,也是這樣做的。
總結
千辛萬苦終於解決了小師妹的問題,F師兄要休息一下。
本文的例子https://github.com/ddean2009/learn-java-io-nio
本文作者:flydean程序那些事
本文鏈接:http://www.flydean.com/io-charsets-properties/
本文來源:flydean的博客
歡迎關注我的公眾號:程序那些事,更多精彩等着您!