什麼是字元,什麼是位元組?
可以理解為電腦沒有字元的概念,只有位元組。字元是存在於人類語言層的概念,其作用是為了人與人之間的交流,因為位元組對於人類是不可讀的,但是電腦儲存所有的資料都是按照位元組儲存。
因 此要將人類意識中的字元儲存到電腦中,則必須將字元轉換為位元組資料,那麼怎麼轉化呢,則必須要一種映射規則,這裡的映射規則就是通常意義中的字元編碼, 比如說該檔案是GBK編碼,可以說為:這個文檔中的字元資料是按照GBK這種字元位元組映射規則將字元轉換為位元組儲存的。
所以所有要將人類意識中的字元儲存在電腦或者需要通過電腦傳遞時,都涉及到字元和位元組之間的通過某種映射規則的轉換。
將字元按照映射規則轉化為位元組稱為編碼,反之稱為解碼。
弄明白了字元和位元組,以及為什麼要編碼解碼的意義,在看在java中哪些地方需要編碼:
1:java的原始碼檔案.我們在使用編輯器編輯資料後,需要將編輯器中的”字元們“儲存起來時,需要選擇一種映射規則儲存在電腦中。當編譯 java原始碼時,javac讀取原始碼檔案,得到原始碼檔案的位元組資料後,需要將其轉換為字元,那麼就必須按照剛剛儲存原始碼時選擇的映射規則同樣的
映射規則才能將這些位元組正確還原為人類意識中的字元,然後再將這些字元使用utf-16映射規則映射為位元組儲存在.class中。所以在編譯java原始碼時,必須指定原始碼的字元位元組映射規則(編碼),如果指定錯了,那麼映射回來的字元就會出錯。
注:javac預設採用本地編譯平台的編碼讀取原始碼檔案。所以如果Team Dev中有些人是日文系統,有些是中文系統,但是又沒有統一原始碼編碼,上傳到cvs後,後來又在utf-8上編譯,呵呵,全亂了。
2:控制台的編碼:當我們在java代碼中使用System.out.println();輸出字元時,向作業系統傳遞資料,讓作業系統再顯示出 來。這中間也存在編碼。記住:所有涉及字元的地方都涉及編碼。因為底層都是通過位元組傳遞的,要通過位元組傳遞就必須選擇一種字元位元組映射規則。
試想在java端我們採用一種映射方式將字元對應表為位元組,將這些位元組發送給作業系統,作業系統得到位元組資料,然後再使用某種映射方式將位元組映射回字元。如果採用的映射方式不對也就會產生顯示出不可預測的資料,不是使用者希望的結果,這就是亂碼
而java實現的時候是採用的本地作業系統預設的映射方式將字元對應表為位元組,作業系統也是採用預設的將位元組映射回字元,因此這個過程中是不會出現任何錯誤的。那麼我們在控制台看見的亂碼是因為在java記憶體中還是字元時這個時候這個字元就已經不是你所希望看見的了。
如上面說的原始碼編碼,如果編譯的時候指定編碼錯誤,那麼將原始碼位元組映射為字元就已經出錯,然後將錯誤的字元採用utf-16映射為了位元組,啟動並執行時候 再將這些位元組映射為字元,這個時候的字元就已經是錯的了(這個字元是本來是utf-16映射規則映射成的位元組但是後來又按照gbk映射
規則映射成的字元。和控制台出現亂碼容易混淆的情況是遠端控制的時候如使用ssh,因為這個時候又多了一層ssh服務端向ssh用戶端發送將字元按照某種映射規則映射成的位元組資料和ssh用戶端選取一種映射規則將位元組映射為字元的情況。
3:JSP 檔案的編碼。在JSP中使用pageEncoding指定jsp源檔案的編碼。原理和上面第一條中的java原始碼一樣。但是為什麼jsp需要 而.java檔案不需要呢。因為jsp是在伺服器上編譯的。你在本地機上寫jsp檔案,儲存的為預設編碼GBK,到伺服器上以後萬一伺服器為utf-8編 碼,如果採用和.java一樣的策略不是就會出錯了嗎。
4:web服務端向瀏覽器發送資料:因為要通過網路傳遞字元資料,所以需要將字元按照某種方式映射為位元組,在瀏覽器端,瀏覽器接收到位元組資料再選取某種映射方式反映射為字元供使用者觀看。同樣,如果選取的影射方式不匹配就會出錯。所以可以讓伺服器將編碼方式告訴用戶端。在HTTP的報文頭中可以包含這些資訊(如Content-Type: text/html;charset=GBK),這可以通過response.setContentType實現(在jsp中使用<%@ page contentType=""%>指令將轉化為response.setContentType代碼)。如果在jsp中指定了 pageEncoding但是沒有指定contentType,產生的servlet代碼中預設使用pageEncoding的編碼設定 contentType.如果沒有設定contentType伺服器(tomcat)預設設定為iso-8859-1。
5:瀏覽器向伺服器發送的資料(和瀏覽器以及瀏覽器的配置相關):同樣也要通過網路傳輸。所以需要知道用戶端採用的是什麼映射規則將使用者輸入的字元 映射為位元組傳遞給服務端.用戶端一般採用的是該頁面的編碼發送資料(這個我沒有認真測試過),但是web伺服器(tomcat)確是預設採用iso- 8859-1映射規則,所以雙方的映射規則不一致。解決方案就是
request.setCharacterEncoding設定伺服器針對瀏覽 器發送來的位元組資料的映射規則(不同的伺服器該方法實現策略可能不一樣,在項目中遇到過weblogic和tomcat不一樣,weblogic該方法會 將HTTP頭和內容塊資料都採用該方法的參數設定映射規則,但是在tomcat中該映射規則只用於內容塊,而報文頭的映射規則在server.xml的 connector的URIEncoding配置).
不同的瀏覽器可能也存在不同。我在IE6中測試伺服器發送utf-8的編碼,通過抓包發現 伺服器發送下來的資料的確是utf-8的,但是如果通過url傳遞如<a href="1.jsp?a=趁"/>結果發現IE傳遞的是e8,b6(少了一個位元組),而"趁"的utf-8編碼是e8,b6,81。但是如果通 過form提交,不管是用get/post方法,傳遞的都是"%e8,%b6,%81"
這種以“%”開始的編碼是將二進位使用十六進位表示。但是在opera中,上面的三種方法都傳遞的是"%e8,%b6,%81".那麼如果伺服器發送的是gbk編碼呢?呵呵自己測試試
6:javascript編碼:javascript和瀏覽器也有關係,IE中,如使用XMLHTTPRequest的open方法調用 open("GET", "2.jsp?a=人");雖然當前頁面是utf-8編碼,但是javascript傳遞的是c8cb,這是"人"的gbk編碼。而在opera中傳遞的 是%E4%BA%BA,是正確的.如果伺服器發送的位元組是utf-16編碼,IE中仍然傳遞的是"人"的gbk編碼,opera也仍然傳遞的是utf-8 的編碼。
不知道瀏覽器是不是可以在哪兒配置
7:資料庫編碼:資料庫不好測試,因為還涉及到資料庫用戶端編碼的問題。如果將正確的字元按照資料庫指定的編碼儲存在資料庫檔案中,當查詢資料時, 資料庫伺服器將正確的字元發送給了用戶端,但是用戶端字元位元組映射規則如果設定錯誤,可能會導致使用者錯誤地認為資料庫中儲存的是錯誤的字元。最好的辦法是 抓包分析,但是資料庫協議非常複雜,又無法抓包,
但是如果在某個過程中發生了出錯,原理和上面描述的一樣,一定是瀏覽器<->web伺服器,web伺服器<->資料庫,資料庫<->資料庫用戶端,ssh用戶端<->ssh服務端等等之間某個地方出現了問題。
要解決雙方選擇的字元位元組映射規則不一致而導致的亂碼問題:
1:是告訴對方自己採用的什麼映射規則將字元轉換為位元組的
2:用協議約定,約定雙方都用某個規則映射
3:類似於XML在第一行標誌下面的資料是採用什麼映射規則映射的
4:在檔案頭儲存特殊位元組標誌映射規則,如windows處理utf-16(會導致如果"聯通"兩個字出現在檔案開始的話出現問題,大家可以到網上搜尋一下這個情況,分析分析,呵呵)