Java Web中的中文编码
Java Web開發(fā)中經(jīng)常會遇到中文編碼問題,那么為什么需要編碼呢?因為人類需要表示的符號太多,無法用1個字節(jié)來表示,而計算機中存儲信息最小單元為1個字節(jié)。所以必須指定char與byte之間的編碼規(guī)則了。
1 常見的編碼方式
計算機中提供了多種編碼方式,常見的有ASCII、ISO-8859-1、GBK、GB2312、UTF-16、UTF-8等。
- ASCII 碼
- 學(xué)過計算機的人都知道 ASCII 碼,總共有 128 個,用一個字節(jié)的低 7 位表示,0~31 是控制字符如換行回車刪除等;32~126 是打印字符,可以通過鍵盤輸入并且能夠顯示出來。
- ISO-8859-1
- 128 個字符顯然是不夠用的,于是 ISO 組織在 ASCII 碼基礎(chǔ)上又制定了一些列標(biāo)準(zhǔn)用來擴展 ASCII 編碼,它們是 ISO-8859-1~ISO-8859-15,其中 ISO-8859-1 涵蓋了大多數(shù)西歐語言字符,所有應(yīng)用的最廣泛。ISO-8859-1 仍然是單字節(jié)編碼,它總共能表示 256 個字符。
- GB2312
- 它的全稱是《信息交換用漢字編碼字符集 基本集》,它是雙字節(jié)編碼,總的編碼范圍是 A1-F7,其中從 A1-A9 是符號區(qū),總共包含 682 個符號,從 B0-F7 是漢字區(qū),包含 6763 個漢字。
- GBK
- 全稱叫《漢字內(nèi)碼擴展規(guī)范》,是國家技術(shù)監(jiān)督局為 windows95 所制定的新的漢字內(nèi)碼規(guī)范,它的出現(xiàn)是為了擴展 GB2312,加入更多的漢字,它的編碼范圍是 8140~FEFE(去掉 XX7F)總共有 23940 個碼位,它能表示 21003 個漢字,它的編碼是和 GB2312 兼容的,也就是說用 GB2312 編碼的漢字可以用 GBK 來解碼,并且不會有亂碼。
- GB18030
- 全稱是《信息交換用漢字編碼字符集》,是我國的強制標(biāo)準(zhǔn),它可能是單字節(jié)、雙字節(jié)或者四字節(jié)編碼,它的編碼與 GB2312 編碼兼容,這個雖然是國家標(biāo)準(zhǔn),但是實際應(yīng)用系統(tǒng)中使用的并不廣泛。
- UTF-16
- 說到 UTF 必須要提到 Unicode(Universal Code 統(tǒng)一碼),ISO 試圖想創(chuàng)建一個全新的超語言字典,世界上所有的語言都可以通過這本字典來相互翻譯。可想而知這個字典是多么的復(fù)雜,關(guān)于 Unicode 的詳細(xì)規(guī)范可以參考相應(yīng)文檔。Unicode 是 Java 和 XML 的基礎(chǔ),下面詳細(xì)介紹 Unicode 在計算機中的存儲形式。
- UTF-16 具體定義了 Unicode 字符在計算機中存取方法。UTF-16 用兩個字節(jié)來表示 Unicode 轉(zhuǎn)化格式,這個是定長的表示方法,不論什么字符都可以用兩個字節(jié)表示,兩個字節(jié)是 16 個 bit,所以叫 UTF-16。UTF-16 表示字符非常方便,每兩個字節(jié)表示一個字符,這個在字符串操作時就大大簡化了操作,這也是 Java 以 UTF-16 作為內(nèi)存的字符存儲格式的一個很重要的原因。
- UTF-8
- UTF-16 統(tǒng)一采用兩個字節(jié)表示一個字符,雖然在表示上非常簡單方便,但是也有其缺點,有很大一部分字符用一個字節(jié)就可以表示的現(xiàn)在要兩個字節(jié)表示,存儲空間放大了一倍,在現(xiàn)在的網(wǎng)絡(luò)帶寬還非常有限的今天,這樣會增大網(wǎng)絡(luò)傳輸?shù)牧髁?#xff0c;而且也沒必要。而 UTF-8 采用了一種變長技術(shù),每個編碼區(qū)域有不同的字碼長度。不同類型的字符可以是由 1~6 個字節(jié)組成。
2 編碼的場景
在IO操作中我們一般需要進行編碼,這里的IO操作包括磁盤IO、網(wǎng)絡(luò)IO等,比如下面就是一個磁盤IO的例子:
public static void main(String[] args) throws IOException {String file = "H:/name.txt";String charset = "utf-8";// 寫字符到字節(jié)流FileOutputStream outputStream = new FileOutputStream(file);OutputStreamWriter writer = new OutputStreamWriter(outputStream, charset);try {writer.write("中文名杭州");}finally {writer.close();}// 讀字節(jié)流到字符FileInputStream inputStream = new FileInputStream(file);InputStreamReader reader = new InputStreamReader(inputStream, charset);try {char[] buffer = new char[64];reader.read(buffer);System.out.println(buffer);}finally {reader.close();} }在程序中設(shè)計IO編解碼,只要我們指定統(tǒng)一的編解碼Charset字符集,一般不會有問題(或者編解碼都是在同一個系統(tǒng)中,都使用默認(rèn)的字符集)。但是如果我們指定的編解碼不一致,就會出現(xiàn)中文亂碼問題。如果把代碼中的InputStreamReader指定為GBK編碼,就會出現(xiàn)亂碼問題。
InputStreamReader reader = new InputStreamReader(inputStream, "GBK");Java中使用String類型也是可以指定字節(jié)到字符轉(zhuǎn)換的編碼字符集的。
1 String xxx = "你好"; 2 String yyy = new String(xxx.getBytes(), "utf-8"); 3 System.out.println(yyy);?
3 幾種編碼格式比較
對中文字符GBK/GB2312/UTF-16/UTF-8四種編碼格式都能處理,GB2312 與 GBK 編碼規(guī)則類似,但是 GBK 范圍更大,它能處理所有漢字字符,所以 GB2312 與 GBK 比較應(yīng)該選擇 GBK。UTF-16 與 UTF-8 都是處理 Unicode 編碼,它們的編碼規(guī)則不太相同,相對來說 UTF-16 編碼效率最高,字符到字節(jié)相互轉(zhuǎn)換更簡單,進行字符串操作也更好。它適合在本地磁盤和內(nèi)存之間使用,可以進行字符和字節(jié)之間快速切換,如 Java 的內(nèi)存編碼就是采用 UTF-16 編碼。但是它不適合在網(wǎng)絡(luò)之間傳輸,因為網(wǎng)絡(luò)傳輸容易損壞字節(jié)流,一旦字節(jié)流損壞將很難恢復(fù),想比較而言 UTF-8 更適合網(wǎng)絡(luò)傳輸,對 ASCII 字符采用單字節(jié)存儲,另外單個字符損壞也不會影響后面其它字符,在編碼效率上介于 GBK 和 UTF-16 之間,所以 UTF-8 在編碼效率上和編碼安全性上做了平衡,是理想的中文編碼方式。
?
4 常見問題分析
當(dāng)我們碰到一些亂碼時,應(yīng)該怎么處理這些問題?出現(xiàn)亂碼問題唯一的原因都是在 char 到 byte 或 byte 到 char 轉(zhuǎn)換中編碼和解碼的字符集不一致導(dǎo)致的,由于往往一次操作涉及到多次編解碼,所以出現(xiàn)亂碼時很難查找到底是哪個環(huán)節(jié)出現(xiàn)了問題,下面就幾種常見的現(xiàn)象進行分析。
中文變成了看不懂的字符
例如,字符串“淘!我喜歡!”變成了“ì ? £ ?? ò ?2?? £ ?”編碼過程如下圖所示
字符串在解碼時所用的字符集與編碼字符集不一致導(dǎo)致漢字變成了看不懂的亂碼,而且是一個漢字字符變成兩個亂碼字符。
一個漢字變成一個問號
例如,字符串“淘!我喜歡!”變成了“??????”編碼過程如下圖所示:
將中文和中文符號經(jīng)過不支持中文的 ISO-8859-1 編碼后,所有字符變成了“?”,這是因為用 ISO-8859-1 進行編解碼時遇到不在碼值范圍內(nèi)的字符時統(tǒng)一用 3f 表示,這也就是通常所說的“黑洞”,所有 ISO-8859-1 不認(rèn)識的字符都變成了“?”。
一個漢字變成兩個問號
例如,字符串“淘!我喜歡!”變成了“????????????”編碼過程如下圖所示:
這種情況比較復(fù)雜,中文經(jīng)過多次編碼,但是其中有一次編碼或者解碼不對仍然會出現(xiàn)中文字符變成“?”現(xiàn)象,出現(xiàn)這種情況要仔細(xì)查看中間的編碼環(huán)節(jié),找出出現(xiàn)編碼錯誤的地方。
?
參考:
1、深入分析 Java 中的中文編碼問題
2、《深入分析Java Web技術(shù)內(nèi)幕》
轉(zhuǎn)載于:https://www.cnblogs.com/luoxn28/p/5933784.html
創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎勵來咯,堅持創(chuàng)作打卡瓜分現(xiàn)金大獎總結(jié)
以上是生活随笔為你收集整理的Java Web中的中文编码的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 详解HashMap数据结构实现
- 下一篇: POJ 2389Bull Math(水~