1. 程式人生 > >各種亂碼問題及原理,很全面

各種亂碼問題及原理,很全面

一、編碼程序

【01編碼】——很久很久以前,為了表示二極體的通、分,我們引入的高電平、低電平,之後又引入的1、0編碼進行代替

 

【ASCII編碼】——很久以前,也就是上個世紀60年代,美國佬為了把計算機的“0101010”編碼與文字進行對應起來,制定了一套ASCII編碼方案。人總是自私的,他只對自己的語言進行編碼,26個字母、數字、其他符號,只用了7位二進位制數搞定,第一位用0表示,預留著。所以ASCII編碼最多127編碼

 

【“二代”ASCII編碼】——不單單是美國佬想把計算機語言和英文聯絡起來,其他國家也想把自己的語言關聯起來,我法文、俄文就基於ASCII編碼,利用上ASCII第一位(未使用)變成1,來對自己國家的語言進行編碼。

 

介樣,每一個國家都基於ASCII,讓第一位變成1,完成對本國的語言的編碼。由於他們各幹各的,沒有溝通,從而導致了同一種編碼出現不同的文字。

 

【gb2312編碼】——我們偉大的祖國80年代也開始對漢字進行編碼,由於我們的文字較多,即使是基於ASCII將第一位變成1,也不夠我們使用(國語博大精深拉),所以就制定了一套gb2312編碼,使用2個位元組表示。

 

【BIG5編碼】——我國的港澳地區,他們是使用繁體字(gb2312編碼最初並沒有考慮到繁體字),怎麼辦了?他們就出了自己的區域編碼BIG5

 

【中國一統GBK編碼】——為了統一漢字,迫切的需要設計出一種既能支援簡體字又能表示繁體的新編碼方案,GBK誕生了!他相容了絕大部分gb2312編碼(gb2312編碼的文字用gbk可以讀出來,但是不相容BIG5編碼)

 

【世界大統unicode編碼】——各國編碼各做各的,總不是意見好事,為了便於交流,國際社會引入了unicode——(uni統一的意思,code編碼)把所有國家的文字都進行了編碼。

 

【統一後浪費空間問題】——統一是一件好事,但是也是有問題的。英文只要1個位元組ASCII編碼就可以表示,你unicode還需要2個位元組或更多,導致unicode表示英文的時候前面有很多無用的000000000000000000,對吧。

 

【解決問題,UTF編碼】——解決方法是使用utf8編碼,它是基於unicode編碼上的一種優化,英文使用1個位元組,中文使用2、3個(絕大部分是3個,個別有看到2、4個的一般不用)位元組。好處:我能屈能伸,我可以變化長度來儲存,就不會浪費空間了吧。

 

 

 

 

來看看unicode和utf對應吧

<!--[if !supportLists]-->·         <!--[endif]-->0000 0000-0000 007F | 0xxxxxxx

<!--[if !supportLists]-->·         <!--[endif]-->0000 0080-0000 07FF | 110xxxxx 10xxxxxx

<!--[if !supportLists]-->·         <!--[endif]-->0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx

<!--[if !supportLists]-->·         <!--[endif]-->0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx

 

舉例說明:

“漢”字的Unicode編碼是0x6C49。0x6C49在0x0800-0xFFFF之間,使用用3位元組模板了:1110xxxx 10xxxxxx 10xxxxxx。將0x6C49寫成二進位制是:0110 1100 0100 1001,用這個位元流依次代替模板後面的x,得到:11100110 10110001 10001001,十六進位制E6 B1 89,轉換成10進位制就是230  這個就是我們的UTF8編碼,"漢".getBytes("utf-8") 得到-26 -79 -119,是負數,也是跟這裡有關

漢      Unicode        utf8

        0x6c49         0xe6b189

二、java程式設計的轉碼

   1、JVM記憶體中儲存的文字編碼

       從原始檔àclass檔案過程中的轉碼情況。最終的class檔案都是以unicode編碼的,我們前面所做的工作就是把各種不同的編碼轉換為unicode編碼,比如從GBK轉換為unicode,從UTF-8轉換為unicode。因為只有採用正確的編碼來轉碼才能保證不出現亂碼。Jvm在執行時其內部都是採用unicode編碼的,其實在輸出時,又會做一次編碼的轉換。

 

比如:Sysout.out.println(“我們”)。經過正確的解碼後”我們”是unicode儲存在記憶體中的,但是在向標準輸出(控制檯)輸出時,jvm又做了一次轉碼,它會採用作業系統預設編碼(中文作業系統是GBK),將記憶體中的unicode編碼轉換為GBK編碼,然後輸出到控制檯。

參考文獻:http://www.blogjava.net/zhangchao/archive/2011/05/26/351051.html

 

   2、愛也getBytes,恨也getBytes

       一般我們是通過getBytes來進行編碼,如下

         public byte[] getBytes(String charsetName)       //使用規定的編碼

         public byte[] getBytes(Charset charset) //使用規定的編碼

         public byte[] getBytes() //使用啟動JVM設定的編碼(如果沒有設定一般是系統預設編碼),避免使用,預設編碼和平臺有關

 

    一般我們通過new String來進行解碼,如下

       public String(byte bytes[], String charsetName)        

                  public String(byte bytes[], Charset charset)

                  public String(byte bytes[])

 

    原則1:使用某一種編碼集對文字進行編碼,就得使用同一種編碼集進行解碼

       比如:“漢”是在java檔案中的,此java檔案是utf-8或者gbk編碼的

         getBytes使用utf-8編碼,new String使用utf-8解密

    new String("漢".getBytes("utf-8"), "utf-8"));

       

    原則2:看清楚對方給你的是什麼編碼,使用逆過程進行編碼、解碼(像出棧操作),最終得到你想要文字

    比如:對方進行了如下操作,將“漢”進行utf-8編碼,然後使用iso8859-1進行解碼生成對應的文字:,最後通過文字傳送給你。對方可能的操作應該是:

         String x = new String("漢".getBytes("utf-8"), "iso8859-1");

 

         我們需要對文字中文字x:進行iso8859-1編碼,得到的是“漢”utf-8的譯碼,然後我們在對“漢”進行解碼得到其utf8的文字

    new String(x.getBytes("iso8859-1"),"utf-8"));

 

    原則3:慎用getBytes(),他是基於平臺的,你得看看JVM啟動的時候file.encoding這個屬性值到底是什麼(請注意tomcat的啟動JVM時候設定的編碼)

    原始碼:特別注意黃底部分

staticbyte[] encode(char[] ca, int off, int len) {

    String csn = Charset.defaultCharset().name();

    try {

        return encode(csn, ca, off, len);

    } catch (UnsupportedEncodingException x) {

        warnUnsupportedCharset(csn);

    }

    try {

        return encode("ISO-8859-1", ca, off, len);

    } catch (UnsupportedEncodingException x) {

        // If this code is hit during VM initialization, MessageUtils is

        // the only way we will be able to get any kind of error message.

        MessageUtils.err("ISO-8859-1 charset not available: "

                + x.toString());

        // If we can not find ISO-8859-1 (a required encoding) then things

        // are seriously wrong with the installation.

        System.exit(1);

        returnnull;

    }

publicstatic Charset defaultCharset() {

        if (defaultCharset == null) {

        synchronized (Charset.class) {

       java.security.PrivilegedAction pa =

           new GetPropertyAction("file.encoding");

       String csn = (String)AccessController.doPrivileged(pa);

       Charset cs = lookup(csn);

       if (cs != null)

           defaultCharset = cs;

                else

           defaultCharset = forName("UTF-8");

            }

    }

    return defaultCharset;

}

}

-Dfile.encoding解釋:
在命令列中輸入java,在給出的提示中會出現-D的說明:
-D<name>=<value>
set a system property
-D後面需要跟一個鍵值對,作用是通過命令列向java虛擬機器傳遞一項系統屬性
對-Dfile.encoding=UTF-8來說就是設定系統屬性file.encoding為UTF-8

 

在java.nio.charset包中的Charset.java中。這段話的意思說的很明確了,簡單說就是預設字符集是在java虛擬機器啟動時決定的,後面無法動態改變了。預設字符集就是從file.encoding這個屬性中獲取的。

 

Java's file.encoding property on Windows platform
This property is used for the default encoding in Java, all readers and writers would default to using this property. file.encoding is set to the default locale of Windows operationg system since Java 1.4.2. System.getProperty("file.encoding") can be used to access this property. Code such as System.setProperty("file.encoding", "UTF-8") can be used to change this property. However, the default encoding can be not changed dynamically even this property can be changed. So the conclusion is that the default encoding can't change after JVM starts. java -dfile.encoding=UTF-8 can be used to set the default encoding when starting a JVM. I have searched for this option Java official documentation. But I can't find it.

 

參考文獻:http://www.cnblogs.com/vigarbuaa/archive/2012/04/11/2442582.html

三、url網址的編碼規範

一、url編碼規範

一般來說,URL只能使用英文字母、阿拉伯數字和某些標點符號,不能使用其他文字和符號。比如,世界上有英文字母的網址“http://www.abc.com”,但是沒有希臘字母的網址“http://www.aβγ.com”(讀作阿爾法-貝塔-伽瑪.com)。這是因為網路標準RFC 1738做了硬性規定:

"...Only alphanumerics [0-9a-zA-Z], the special characters "$-_.+!*'()," [not including the quotes - ed], and reserved characters used for their reserved purposes may be used unencoded within a URL."

“只有字母和數字[0-9a-zA-Z]、一些特殊符號“$-_.+!*'(),”[不包括雙引號]、以及某些保留字,才可以不經過編碼直接用於URL。”

這意味著,如果URL中有漢字,就必須編碼後使用。但是麻煩的是,RFC 1738沒有規定具體的編碼方法,而是交給應用程式(瀏覽器)自己決定。這導致“URL編碼”成為了一個混亂的領域。

 

參考文獻:http://www.ruanyifeng.com/blog/2010/02/url_encoding.html

四、瀏覽器的編碼

下面就讓我們看看,“URL編碼”到底有多混亂。我會依次分析四種不同的情況,在每一種情況中,瀏覽器的URL編碼方法都不一樣。把它們的差異解釋清楚之後,我再說如何用Javascript找到一個統一的編碼方法。

二、情況1:網址路徑中包含漢字

開啟IE(我用的是8.0版),輸入網址“http://zh.wikipedia.org/wiki/春節”。注意,“春節”這兩個字此時是網址路徑的一部分。

 

檢視HTTP請求的頭資訊,會發現IE實際查詢的網址是“http://zh.wikipedia.org/wiki/%E6%98%A5%E8%8A%82”。也就是說,IE自動將“春節”編碼成了“%E6%98%A5%E8%8A%82”。

 

我們知道,“春”和“節”的utf-8編碼分別是“E6 98 A5”和“E8 8A 82”,因此,“%E6%98%A5%E8%8A%82”就是按照順序,在每個位元組前加上%而得到的。(具體的轉碼方法,請參考我寫的《字元編碼筆記》。)

在Firefox中測試,也得到了同樣的結果。所以,結論1就是,網址路徑的編碼,用的是utf-8編碼。

三、情況2:查詢字串包含漢字

在IE中輸入網址“http://www.baidu.com/s?wd=春節”。注意,“春節”這兩個字此時屬於查詢字串,不屬於網址路徑,不要與情況1混淆。

 

檢視HTTP請求的頭資訊,會發現IE將“春節”轉化成了一個亂碼。

 

切換到十六進位制方式,才能清楚地看到,“春節”被轉成了“B4 BA BD DA”。

 

我們知道,“春”和“節”的GB2312編碼(我的作業系統“Windows XP”中文版的預設編碼)分別是“B4 BA”和“BD DA”。因此,IE實際上就是將查詢字串,以GB2312編碼的格式傳送出去。

Firefox的處理方法,略有不同。它傳送的HTTP Head是“wd=%B4%BA%BD%DA”。也就是說,同樣採用GB2312編碼,但是在每個位元組前加上了%。

 

所以,結論2就是,查詢字串的編碼,用的是作業系統的預設編碼。(我使用谷歌瀏覽器,他會是使用utf-8編碼的)

四、情況3:Get方法生成的URL包含漢字

前面說的是直接輸入網址的情況,但是更常見的情況是,在已開啟的網頁上,直接用Get或Post方法發出HTTP請求。

根據臺灣中興大學呂瑞麟老師的試驗,這時的編碼方法由網頁的編碼決定,也就是由HTML原始碼中字符集的設定決定。

<meta http-equiv="Content-Type" content="text/html;charset=xxxx">

如果上面這一行最後的charset是UTF-8,則URL就以UTF-8編碼;如果是GB2312,URL就以GB2312編碼。

舉例來說,百度是GB2312編碼,Google是UTF-8編碼。因此,從它們的搜尋框中搜索同一個詞“春節”,生成的查詢字串是不一樣的。

百度生成的是%B4%BA%BD%DA,這是GB2312編碼。

 

Google生成的是%E6%98%A5%E8%8A%82,這是UTF-8編碼。

 

所以,結論3就是,GET和POST方法的編碼,用的是網頁的編碼。

 

參考文獻:http://www.ruanyifeng.com/blog/2010/02/url_encoding.html

五、網頁、js編碼

五、情況4:Ajax呼叫的URL包含漢字

前面三種情況都是由瀏覽器發出HTTP請求,最後一種情況則是由Javascript生成HTTP請求,也就是Ajax呼叫。還是根據呂瑞麟老師的文章,在這種情況下,IE和Firefox的處理方式完全不一樣。

舉例來說,有這樣兩行程式碼:

url = url + "?q=" +document.myform.elements[0].value; // 假定使用者在表單中提交的值是“春節”這兩個字

http_request.open('GET', url, true);

那麼,無論網頁使用什麼字符集,IE傳送給伺服器的總是“q=%B4%BA%BD%DA”,而Firefox傳送給伺服器的總是“q=%E6%98%A5%E8%8A%82”。也就是說,Ajax呼叫中,IE總是採用GB2312編碼(作業系統的預設編碼),而Firefox總是採用utf-8編碼。這就是我們的結論4。

六、Javascript函式:escape()

好了,到此為止,四種情況都說完了。

假定前面你都看懂了,那麼此時你應該會感到很頭痛。因為,實在太混亂了。不同的作業系統、不同的瀏覽器、不同的網頁字符集,將導致完全不同的編碼結果。如果程式設計師要把每一種結果都考慮進去,是不是太恐怖了?有沒有辦法,能夠保證客戶端只用一種編碼方法向伺服器發出請求?

回答是有的,就是使用Javascript先對URL編碼,然後再向伺服器提交,不要給瀏覽器插手的機會。因為Javascript的輸出總是一致的,所以就保證了伺服器得到的資料是格式統一的。

Javascript語言用於編碼的函式,一共有三個,最古老的一個就是escape()。雖然這個函式現在已經不提倡使用了,但是由於歷史原因,很多地方還在使用它,所以有必要先從它講起。

實際上,escape()不能直接用於URL編碼,它的真正作用是返回一個字元的Unicode編碼值。比如“春節”的返回結果是%u6625%u8282,也就是說在Unicode字符集中,“春”是第6625個(十六進位制)字元,“節”是第8282個(十六進位制)字元。

 

它的具體規則是,除了ASCII字母、數字、標點符號“@ * _ + - . /”以外,對其他所有字元進行編碼。在\u0000到\u00ff之間的符號被轉成%xx的形式,其餘符號被轉成%uxxxx的形式。對應的解碼函式是unescape()。

所以,“Hello World”的escape()編碼就是“Hello%20World”。因為空格的Unicode值是20(十六進位制)。

 

還有兩個地方需要注意。

首先,無論網頁的原始編碼是什麼,一旦被Javascript編碼,就都變為unicode字元。也就是說,Javascipt函式的輸入和輸出,預設都是Unicode字元。這一點對下面兩個函式也適用。

 

其次,escape()不對“+”編碼。但是我們知道,網頁在提交表單的時候,如果有空格,則會被轉化為+字元。伺服器處理資料的時候,會把+號處理成空格。所以,使用的時候要小心。

七、Javascript函式:encodeURI()

encodeURI()是Javascript中真正用來對URL編碼的函式。

它著眼於對整個URL進行編碼,因此除了常見的符號以外,對其他一些在網址中有特殊含義的符號“; / ? : @ & = + $ , #”,也不進行編碼。編碼後,它輸出符號的utf-8形式,並且在每個位元組前加上%。

 

它對應的解碼函式是decodeURI()。

 

需要注意的是,它不對單引號'編碼。

八、Javascript函式:encodeURIComponent()

最後一個Javascript編碼函式是encodeURIComponent()。與encodeURI()的區別是,它用於對URL的組成部分進行個別編碼,而不用於對整個URL進行編碼。

因此,“; / ? : @ & = + $ , #”,這些在encodeURI()中不被編碼的符號,在encodeURIComponent()中統統會被編碼。至於具體的編碼方法,兩者是一樣。

 

它對應的解碼函式是decodeURIComponent()。

 

參考文獻:http://www.ruanyifeng.com/blog/2010/02/url_encoding.html

六、tomcat容器的編碼

   1、啟動tomcat容器時的編碼

    在第二節中,說道:慎用getBytes(),他是基於平臺的,tomcat中JVM啟動的時候file.encoding這個屬性值到底是什麼,由誰決定?

       Linux修改catalina.sh檔案

JAVA_OPTS=”-server -Dfile.encoding=GBK -Xms=512m -Xmx1024m -XX:PermSize=128m -XX:MaxPermSize=256m -verbose:gc -Xloggc:${CATALINA_HOME}/logs/gc.log`date +%Y-%m-%d-%H-%M` -XX:+UseConcMarkSweepGC -XX:+CMSIncrementalMode -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -noclassgc”

      

    如果你像上面這樣告訴JVM,啟動我tomcat容器的時候,使用GBK來編碼,後續的getBytes返回的就是GBK編碼的東西,這點特別在部署的tomcat的時候注意。

 

   2、tomcat對錶單中get提交的資料,出亂碼看哪裡?

    get提交的tomcat從4.x之後不是使用request.setCharacterEncoding("字符集"),從這裡獲取,它直接使用直接對引數進行編碼,這個編碼他是從tomcat的配置檔案中獲取<Connector port="8080" protocol="HTTP/1.1" maxThreads="150" connectionTimeout="20000" redirectPort="8443" URIEncoding="UTF-8"/> 

   

   3、tomcat對錶單中post提交的資料,出亂碼看哪裡?

    對於post需要在web.xml裡面配置攔截器,主要是在程式第一次從request裡面獲取引數之前把request給設定了具體的編碼

    publicvoid doFilter(ServletRequest req, ServletResponse resp,FilterChain chain)throws IOException, ServletException {

           req.setCharacterEncoding(encoding);//這裡是過濾器,直接設定編碼

           HttpServletRequest request = (HttpServletRequest)req;

           HttpServletResponse response = (HttpServletResponse)resp;

           ActionContext.setContext(request, response);

          

           chain.doFilter(req,resp);

           ActionContext.removeContext();

    }

 還有一點需要注意的是:你在web.xml裡面配置過濾器的優先順序是不是最高,一定要在request出現之前攔截。

   4、tomcat對jsp頁面的編碼,看哪裡?

    你還記得jsp頁面中有下面這句話麼?pageEncoding="utf-8"告訴tomcat,你編譯我jsp檔案的時候使用他來編譯,如果這句話沒有,他預設使用charset=utf-8"這句話來編譯,同時注意儲存jsp的時候使用編碼也應該與pageEncoding="utf-8"保持一致

<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%>

 

   你還記得下面這句話麼?由他來告訴瀏覽器使用什麼編碼的

<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />

七、mysql資料庫的編碼

1、檢視資料庫編碼

需要以root使用者身份登陸才可以檢視資料庫編碼方式(以root使用者身份登陸的命令為:

>mysql -u root –p,之後兩次輸入root使用者的密碼),檢視資料庫的編碼方式命令為:

 >show variables like 'character%';
+--------------------------+----------------------------+
| Variable_name | Value |
+--------------------------+----------------------------+
| character_set_client | latin1 |
| character_set_connection | latin1 |
| character_set_database | latin1 |
| character_set_filesystem | binary |
| character_set_results | latin1 |
| character_set_server | latin1 |
| character_set_system | utf8 |
| character_sets_dir | /usr/share/mysql/charsets/ |
+--------------------------+----------------------------+

從以上資訊可知資料庫的編碼為latin1,需要修改為gbk或者是utf8;

其中,character_set_client為客戶端編碼方式;character_set_connection為建立連線使用的編碼;character_set_database資料庫的編碼;

character_set_results結果集的編碼;

character_set_server資料庫伺服器的編碼;

只要保證以上四個採用的編碼方式一樣,就不會出現亂碼問題。

 

2、更改資料庫編碼

停止MySQL的執行
/etc/init.d/mysql start (stop) 為啟動和停止伺服器

MySQL主配置檔案為my.cnf,一般目錄為/etc/mysql

var/lib/mysql/ 放置的是資料庫表文件夾,這裡的mysql相當於windows下mysql的date資料夾

當我們需要修改MySQL資料庫的預設編碼時,需要編輯my.cnf檔案進行編碼修改,在linux下修改mysql的配置檔案my.cnf,檔案位置預設/etc/my.cnf檔案

找到客戶端配置[client] 在下面新增
default-character-set=utf8 預設字符集為utf8
在找到[mysqld] 新增
default-character-set=utf8 預設字符集為utf8
init_connect='SET NAMES utf8' (設定連線mysql資料庫時使用utf8編碼,以讓mysql資料庫為utf8執行)

 

3、改了之後,為什麼win裡面cmd中的那個console控制檯還是顯示亂碼了?

         Mysql通過客戶端傳送到控制檯展示之前是utf8,但是console是使用系統的預設編碼(gbk),所以在中文的時候發生了亂碼顯示,但是不影響程式操作,只是顯示的時候有問題。

         如何解決了?

         方法一:(只對當前視窗有用,推薦使用)

         輸入:set names gbk

 

 

         方法二:將控制檯展示編碼改成utf8(對所有的視窗,不推薦)

1、開啟CMD.exe命令列視窗 
2、通過 chcp命令改變內碼表,UTF-8的內碼表為65001 
F:\trash>chcp 65001
執行該操作後,內碼表就被變成UTF-8了。但是,在視窗中仍舊不能正確顯示UTF-8字元。 

3、修改視窗屬性,改變字型 
在命令列標題欄上點選右鍵,選擇"屬性"->"字型",將字型修改為True Type字型"Lucida Console",然後點選確定將屬性應用到當前視窗。 
4、通過以上操作並不能完全解決問題,因為顯示出來的內容有可能不完全。可以先最小化,然後最大化命令列視窗,檔案的內容就完整的顯示出來了。

 

 

 

 

 

 

Java開發中,常常會遇到亂碼的問題,一旦遇到這種問題,常常就很扯蛋,每個人都不願意承認是自己的程式碼有問題。其實編碼問題並沒有那麼神祕,那麼不可捉摸,搞清Java的編碼本質過程就真相大白了。

 

先看個圖:

 

 

其實,編碼問題存在兩個方面:JVM之內和JVM之外。

 

 

1、Java檔案編譯後形成class

這裡Java檔案的編碼可能有多種多樣,但Java編譯器會自動將這些編碼按照Java檔案的編碼格式正確讀取後產生class檔案,這裡的class檔案編碼是Unicode編碼(具體說是UTF-16編碼)。

 

因此,在Java程式碼中定義一個字串:

String s="漢字";

不管在編譯前java檔案使用何種編碼,在編譯後成class後,他們都是一樣的----Unicode編碼表示。

 

2、JVM中的編碼

JVM載入class檔案讀取時候使用Unicode編碼方式正確讀取class檔案,那麼原來定義的String s="漢字";在記憶體中的表現形式是Unicode編碼。

 

當呼叫String.getBytes()的時候,其實已經為亂碼買下了禍根。因為此方法使用平臺預設的字符集來獲取字串對應的位元組陣列。在WindowsXP中文版中,使用的預設編碼是GBK,不信執行下:

public class Test { 
        public static void main(String[] args) { 
                System.out.println("當前JRE:" + System.getProperty("java.version")); 
                System.out.println("當前JVM的預設字符集:" + Charset.defaultCharset()); 
        } 
}

 

當前JRE:1.6.0_16 
當前JVM的預設字符集:GBK

 

當不同的系統、資料庫經過多次編碼後,如果對其中的原理不理解,就容易導致亂碼。因此,在一個系統中,有必要對字串的編碼做一個統一,這個統一模糊點說,就是對外統一。比如方法字串引數,IO流,在中文系統中,可以統一使用GBK、GB13080、UTF-8、UTF-16等等都可以,只是要選擇有些更大字符集,以保證任何可能用到的字元都可以正常顯示,避免亂碼的問題。(假設對所有的檔案都用ASCII碼)那麼就無法實現雙向轉換了。

 

要特別注意的是,UTF-8並非能容納了所有的中文字符集編碼,因此,在特殊情況下,UTF-8轉GB18030可能會出現亂碼,然而一群傻B常常在做中文系統喜歡用UTF-8編碼而不說不出個所以然出來!最傻B的是,一個系統多個人做,原始碼檔案有的人用GBK編碼,有人用UTF-8,還有人用GB18030。FK,都是中國人,也不是外包專案,用什麼UTF-8啊,神經!原始碼統統都用GBK18030就OK了,免得ANT指令碼編譯時候提示不可認的字元編碼。

 

因此,對於中文系統來說,最好選擇GBK或GB18030編碼(其實GBK是GB18030的子集),以便最大限度的避免亂碼現象。

 

3、記憶體中字串的編碼

記憶體中的字串不僅僅侷限於從class程式碼中直接載入而來的字串,還有一些字串是從文字檔案中讀取的,還有的是通過資料庫讀取的,還有可能是從位元組陣列構建的,然而他們基本上都不是Unicode編碼的,原因很簡單,儲存優化。

 

因此就需要處理各種各樣的編碼問題,在處理之前,必須明確“源”的編碼,然後用指定的編碼方式正確讀取到記憶體中。如果是一個方法的引數,實際上必須明確該字串引數的編碼,因為這個引數可能是另外一個日文系統傳遞過來的。當明確了字串編碼時候,就可以按照要求正確處理字串,以避免亂碼。

在對字串進行解碼編碼的時候,應該呼叫下面的方法:

getBytes(String charsetName)    
String(byte[] bytes, String charsetName)

 

而不要使用那些不帶字符集名稱的方法簽名,通過上面兩個方法,可以對記憶體中的字元進行重新編碼。