一、字符集
規(guī)定了某個文字對應的二進制數(shù)字存放方式(編碼)和某串二進制數(shù)值代表了哪個文字(解碼)的轉(zhuǎn)換關系。對應到真實生活中,字符集就是對某種語言的稱呼。例如:英語,漢語,日語。
Unicode是為了統(tǒng)一ASCII(僅英文字母)GBK(中文)等而發(fā)明的,就是要把地球上所有的語言的符號,都用統(tǒng)一的字符集來表示,一個編碼真正做到了唯一。
不過,Unicode只是一個符號集,只規(guī)定了符號的 二進制代碼,卻沒有規(guī)定這個二進制代碼應該如何存儲
二、字符編碼
對于一個字符集來說要正確編碼轉(zhuǎn)碼一個字符需要三個關鍵元素:字庫表(character
repertoire)、編碼字符集(coded character
set)、字符編碼(character
encoding form)。
- 字庫表:所有的單詞,(為了能夠涵蓋世界上所有的字符)
- 編碼字符集:單詞在字庫的哪里,索引id
- 字符編碼:把序號轉(zhuǎn)換成另外一種存儲格式,(真正用的上的字符相對整個字庫表來說比例非常低),是字符集的實現(xiàn)方式,因為計算機只能看懂bytes。我們需要一種用bytes來表示Unicode的方法這樣才可以存儲和傳播他們。
UTF-8就是字符編碼,變長。UTF-8是Unicode的實現(xiàn)方式之一
i) 好處一是節(jié)省流量。
ii) 好處二是ASCII編碼實際上可以被看成是UTF-8編碼的一部分,所以,大量只支持ASCII編碼的歷史遺留軟件可以在UTF-8編碼下繼續(xù)工作。
iii) UTF-8編碼能表示的字符數(shù)量遠超GBK。
三、 Window筆記本
Windows 記事本里面的4個選項:
- ANSI,俗稱“默認編碼”,不是確定的一種編碼,是Windows提出來的一種解決方案,會根據(jù)不同的語言版本選擇。對于英文文件,是ASCII編碼,對于簡體中文是GB2312,繁體中文是Big5,日文是JIS
- Unicode, 這個選項用的是little endian
- Unicode big endian
- UTF-8
四、JAVA的內(nèi)部編碼
既然計算機在內(nèi)存中統(tǒng)一使用的是unicode編碼,JAVA自然也是如此了。
Java 內(nèi)部處理字符使用的字序方式是 Unicode,這是一種通行全球的編碼方式。 Unicode 另有一種儲存或傳輸?shù)母袷剑凶?UTF-8。UTF-8 的格式在編碼英文時,只需要 8 位,但是中文則是 24 位,所以中文字出現(xiàn)比例高的地方還是使用 UTF-16 比較節(jié)省空間。Java 的 Class File(也就是 bytecode)中有一字段叫做常數(shù)區(qū)(Constant Pool),一律使用 UTF-8 為字符編碼。
五、 JAVA 的I/O 編碼
雖然 Java 內(nèi)部完整地使用 Unicode,但是你所使用的操作系統(tǒng)可不見得。
Java 現(xiàn)行的 IO 一律使用 Stream 的方式,相關的類別都放在 java.io 中。
輸出 binary 的使用 OutputStream 的子類別,輸入 binary 的資料使用 InputStream 的子類別;
輸出文字的使用 Writer 的子類別,輸入文字的資料使用 Reader 的子類別。
-
InputStream/OutputStream 與 Reader/Writer的區(qū)別
InputStream/OutputStream 會原封不動地傳送資料,但是Reader/Writer 會將資料當作文字對待,所以 Reader/Writer 在「必要時」會把(文字)資料轉(zhuǎn)碼(會根據(jù)操作系統(tǒng)的默認編碼方式進行轉(zhuǎn)碼)。 -
什么時候轉(zhuǎn)碼?:
Java 的 Stream(包括 Reader 和 Writer)是可以互相串接的。當 Reader 的資料來源是另一個 Reader 時,不轉(zhuǎn)碼,當 Reader 的資料來源是一個 InputStream 時,就會轉(zhuǎn)碼。當 Writer 的資料去處是另一個 Writer 時,不轉(zhuǎn)碼,當 Writer 的資料去處是一個 OutputStream 時,就會轉(zhuǎn)碼。 -
由什么碼轉(zhuǎn)成什么碼?
這是可以指定的。
因為轉(zhuǎn)碼只發(fā)生在 Reader/InputStream 的交界處與 Writer/OutputStream 的交界處,所以正是由 InputStreamReader 和 OutputStreamWriter 此二類別負責,下面兩個 constructor 的第二個參數(shù),正是用來指定轉(zhuǎn)碼的方式。
new InputStreamReader(new FileInputStream(inputFilePath), encoding)
如果你清楚地知道你要讀寫的檔案(或資料來源 / 去處)是采用某種編碼方式,你也可以主動指定編碼方式。但是,請記得抓取可能導致的 UnsupportedEncodingException,并務必處理之,不可對此例外置之不理,因為該 JRE 有可能沒有附上此種編碼表(也有可能你的編碼名稱給錯)。 -
保持現(xiàn)場 ,保持資料的完整性
如果你不知道你的 I/O 資料來源或去處是用何種編碼方式,那么你最好不要用 Reader 和 Writer,而應該直接用 InputStream 和 OutputStream,因為與其被 Reader 和 Writer 胡亂編碼之后造成信息遺失或錯亂,不如保持資料的完整不變,留待以后進一步解讀。 -
建議
在我們的應用程序中涉及到 I/O 操作時只要注意指定統(tǒng)一的編解碼 Charset 字符集,一般不會出現(xiàn)亂碼問題,有些應用程序如果不注意指定字符編碼,中文環(huán)境中取操作系統(tǒng)默認編碼,如果編解碼都在中文環(huán)境中,通常也沒問題,但是還是強烈的不建議使用操作系統(tǒng)的默認編碼,因為這樣,你的應用程序的編碼格式就和運行環(huán)境綁定起來了,在跨環(huán)境下很可能出現(xiàn)亂碼問題。
六、 內(nèi)存操作中的編碼
Java中,String 類就提供轉(zhuǎn)換到字節(jié)的方法,也支持將字節(jié)轉(zhuǎn)換為字符串的構(gòu)造函數(shù)。
Charset 提供 encode 與 decode 分別對應 char[] 到 byte[] 的編碼和 byte[] 到 char[] 的解碼。
參考: http://blog.csdn.net/mazhimazh/article/details/19327421
把一個字符串“中”賦給 String 類的一個對象 str,這個字符串“中”是按照操作系統(tǒng)默認編碼方式進行編碼,在中文 windows 系統(tǒng)中通常是“GBK”,“中”在GBK編碼中是0xD6D0,在將該字符賦給str時,Java會對該字符串進行編碼轉(zhuǎn)換,即將GBK編碼方式的“中”轉(zhuǎn)換成Unicode編碼方式的“中”,Unicode編碼方式“中”的編碼是0x4E2D,所以str在程序運行期間在內(nèi)存中的二進制表示成16進制就是0x4E2D。
七、 造成MySQL亂碼的一個原因
- 存入和取出時對應環(huán)節(jié)的編碼不一致
我們把存入階段的三次編解碼使用的字符集編號為C1,C2,C3;取出時的三個字符集依次編號為C1’,C2’,C3’。那么存入的時候bash C1用的是UTF-8編碼,取出的時候,C1'我們卻使用了windows終端(默認是GBK編碼),那么結(jié)果幾乎一定是亂碼。又或者存入MySQL的時候set names utf8(C2),而取出的時候卻使用了set names gbk(C2'),那么結(jié)果也必然是亂碼 - 單個流程中三步的編碼不一致
即上面任意一個流程同一方向的三步中,只要兩步或者兩部以上的編碼有不一致就有可能出現(xiàn)編解碼錯誤。如果差異的兩個字符集之間無法進行無損編碼轉(zhuǎn)換,那么就一定會出現(xiàn)亂碼。