Java string 字符集編碼以及轉換
基本概念
關於字符集的種類常用的有utf-8, unicode,gbk,gbk2312等,詳細的字符集列表可以查看java.nio.charset.Charset類。
關鍵的字符集處理方法介紹如下:
String.getBytes() | 獲取當前string表示的字符,在使用系統默認的字符集(關於系統默認的字符集後面詳細討論)時,所映射的二進制數據。不同的默認字符集生成的二進制數據是不同的,解碼時只有使用與該默認字符集相同的字符集才能獲得正確的字符。 |
String.getBytes(String charset) | 獲取當前string表示的字符,在使用指定字符集時,所映射的二進制數據。傳入不同的字符集生成的二進制數據是不同,解碼時只有使用相同的字符集才能獲得正確的字符。 |
new String(byte[] data) | 使用系統默認的字符集,將給定的二進制數據映射爲相應的字符,並構造成一個string。如果系統默認的字符集不變那麼可以通過String.getBytes()或String.getBytes(Charset.defaultCharset().displayName())來還原原來的二進制數據。 |
new String(byte[] data, String charset) | 使用給定的字符集,將給定的二進制數據映射爲相應的字符,並構造成一個string。只有通過傳入相同的字符集才能還原原來的二進制數據,String.getBytes("charset")。 |
日常使用場景及解決方案
場景1——修改JVM系統字符集
系統默認的字符集是指,JVM運行時調用java.nio.Charset.defaultCharset().displayName()所顯示的字符集。我們有如下幾種方式更改JVM在運行時的系統字符集:
方法1 | Properties pps=System. getProperties(); pps.put("file.encoding","<your-charset>"); System.setProperties(pps); |
方法2 | System.setProperty("file.encoding","<your-charset>"); |
方法3 | java -D file.encoding=<your-charset> |
上表中尖括弧斜體部分應該替換爲你想要的字符集。
需要注意的是,如果是在運行時更改了字符集,那麼再調用java.nio.Charset.defaultCharset().diaplayName()可能並不會變,因爲Charset源碼對default charset做了內容緩存,具體可查看Charset源碼:
private static volatile Charset defaultCharset; |
因此要獲取更改後的字符集編碼,使用方法1或方法2後,需要使用System.getProperty("file.encoding")獲取最新的字符集。在實際項目中,很多庫都會默認調用String.getBytes(), 而該方法中使用的是java.nio.Charset.defaultCharset().displayName()來獲取默認字符集。具體可查看String類和StringCoding類源碼:
String類
/** |
StringCoding類
static byte[] encode(char[] ca, int off, int len) { |
綜上所述,如果需要完美的修改系統默認的字符集,方法3最好。
場景2——網絡通信的字符集編碼
在開發中,前後端通信時出現亂碼問題,特別是中文更易出現亂碼。一般情況下,都是前後端默認的字符集和業務上需求的字符集不匹配。下面基於一個常見的具體情況,來做說明。
假設服務端默認的字符集編碼爲GBK,前端默認的字符集編碼爲GB2312。
如果雙方都是明文通信,並且http請求和響應的header中都正確指定了字符集(正確指定是指,內容的編碼與header中指定的字符集是匹配的),那麼通信雙方,都從對方發過來的header中拿到charset,然後調用new String(byte[] httpBodyRawData, charset)即可。
如果雙方都是明文通信,但是並沒有正確指定header中的字符集,那麼就需要約定的字符集,如果約定的字符集爲UTF-8, 那麼調用new String(byte[] httpBodyRawData, "UTF-8")即可。
如果雙方使用密文通信,約定的字符集爲UTF-8。假設需要加密的字符串爲toEncryStr, 解密後的原始數據爲byte[] decryData。加密分爲如下幾種情況:
如果加密算法接收byte[]類型,那麼需要:toEncryStr.getBytes("UTF-8")。
如果加密算法接收string類型,加密時調用String.getBytes("UTF-8"),那麼需要做的轉換是: new String(toEncryStr.getBytes("UTF-8"), "UTF-8");
該轉換的意思是,首先將當前字符通過UTF-8字符集映射爲二進制數據(此時只有用UTF-8解碼才能正常顯示),然後將二進制數據通過UTF-8字符集映射爲相應字符(一定是正常字符)。之後加密時,調用String.getBytes("UTF-8")時會將轉換後的字符(正常)還原爲UTF-8編碼的二進制數據。
解碼比較簡單,只需要:new String(decryData, "UTF-8")。// 約定的字符集爲UTF-8