1. 程式人生 > >java中的編碼問題

java中的編碼問題

也有 特殊 ext flush htm java 所有 技術分享 tomcat

一直在試圖搞清楚java中的編碼問題,也看了網上的一些文章,但還是雲裏霧裏。直到最近看了方立勛老師的web課程,才略略明白一點。

在此記錄一下自己的理解,看看自己能不能說清楚。

第一個問題:我在java代碼中定義了一個字符串,它是什麽編碼?

字符串實質是一個char數組。那麽char的編碼,其實就是字符串的編碼。那麽char什麽編碼呢?為什麽‘中‘字轉int類型後的值是20013呢?

        char c = ‘中‘;
        System.out.println(c); //
        System.out.println((int) c); // 20013

引入第一個概念:Unicode編碼

Unicode是一種“編碼”,所謂編碼就是一個編號(數字)到字符的一種映射關系,就僅僅是一種一對一的映射而已。

java的String使用的編碼是Unicode。

來個簡單的例子證明:

找個轉碼工具,將‘中‘字轉為Unicode編碼,結果是‘\u4e2d‘。用‘\u4e2d‘替換原來的‘中‘字。打印的結果和‘中‘字是一樣的。

        char c = ‘\u4e2d‘;
        System.out.println(c); //
        System.out.println((int) c); // 20013

‘\u‘是什麽意思?

‘\u‘的意思就是使用了Unicode編碼。後面加上十六進制代碼來表示Unicode字符。下面這段代碼可以驗證。

        Integer num = Integer.valueOf("4e2d", 16);
        System.out.println(num.intValue()); // 20013

第二個問題:編碼和編碼格式的區別是什麽?

這部分內容來自文章 java中的編碼和編碼格式問題 作者:風未馨

1. Unicode是一種“編碼”,所謂編碼就是一個編號(數字)到字符的一種映射關系,就僅僅是一種一對一的映射而已。

2. GBK、UTF-8是一種“編碼格式”,是用來序列化或存儲1中提到的那個“編號(數字)”的一種“格式”。

編碼和編碼格式

  java的String使用的編碼是Unicode,當String存在於內存中時

(在代碼中用string類型的引用對它進行操作時),是"只有編碼而沒有編碼格式的",所以java程序中的任何String對象,說它是gbk還是utf-8都是錯的,String在內存中不需要“編碼格式”, 它只是一個Unicode的字符串而已。

  當字符串需要在網絡中傳輸或要被寫入文件時,就需要使用編碼格式了。亂碼問題也因此出現。

GBK和UTF-8

  GBK和UTF-8都是用來序列化或存儲Unicode編碼的數據的,但是分別是2種不同的格式,他們都是Unicode編碼的實現方式;他們倆除了格式不一樣之外,他們所關心的Unicode編碼範圍也不一樣。

  UTF-8考慮了很多種不同國家的字符,涵蓋整個unicode碼表,所以其存儲一個字符的編碼的時候,使用的字節長度也從1字節到4字節不等;

  而GBK只考慮中文——在Unicode中的一小部分的字符的編碼,所以它算好了只要2個字節就能涵蓋到絕大多數常用中文(2個字節能表示6w多種字符),所以它存儲一個字符的時候,所用的字節長度是固定的;

ASCII碼和Unicode

  ASCII碼,和Unicode編碼一樣,也是一種"編碼"。

  ASCII碼的範圍比較小,一共規定了128個字符的編碼。

  Unicode編碼是一個很大的集合,現在的規模可以容納100多萬個符號。就像它的名字都表示的,這是一種包含所有符號的編碼。

  當然也有其他的編碼,沒用過,我也不甚了解。

第三個問題:哪些地方會用到編碼格式?

  前文中提到過一句話:當字符串需要在網絡中傳輸或要被寫入文件時,就需要使用編碼格式了。

網絡中傳輸:

對於java web開發人員,指的就是java web了。那在java web中,哪些地方需要設置編碼格式呢?

先上個圖(網上隨便拷貝來的)。假定瀏覽器就是ie,WEB服務器是tomcat,頁面是jsp,應用服務器用的是servlet。

技術分享

1. 瀏覽器中打開了一個頁面,這個頁面映射應用服務的某個jsp。此時瀏覽器中的解碼格式是什麽呢?

瀏覽器的解碼格式是在jsp中指定的,比如在jsp文件中經常可以看到這樣兩行代碼。

他們分別是什麽意思呢?


<!-- 這一行的意思是:這個jsp裏面的內容都是使用utf-8編碼(即內容會以utf-8編碼後發送給瀏覽器) -->
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> 

<!-- 這一行的意思是告訴瀏覽器:你解碼的時候請使用utf-8解碼哦 -->
<meta http-equiv="content-type" content="text/html; charset=UTF-8">

這樣瀏覽在解析這個jsp的時候,就是使用utf-8編碼來解碼。

瀏覽器的解碼格式也是可以在瀏覽器上設置的:

技術分享

如果網頁中出現了亂碼,你換一種編碼格式來解碼,可能就不亂了。

2. response中也可以設置內容的編碼格式和指定瀏覽器的解碼格式。

開發人員應該都知道,jsp就是一個特殊一點的servlet。在servlet中, response有兩個設置編碼的方法,和jsp中的那兩行編碼配置有著完全相同的功能。

它們就是:

        // 表示response的內容會以utf-8的編碼方式編碼後發送給瀏覽器。
     response.setCharacterEncoding("UTF-8");

     // 告訴瀏覽器,解碼的時候也要使用utf-8解碼哦。 response.setContentType(
"text/html;charset=UTF-8");

3. 通過request對象可以指定應用服務用哪種編碼格式來解碼接收到的數據.

上面兩種情況,都是講servlet自己如何編碼,然後告訴瀏覽器如何解碼的。servlet也可以指定對接收到的對象使用哪種編碼格式來解碼。   

    // 通過這句話,可以指定用utf-8編碼格式來解碼瀏覽器傳來的數據。不過只對post方式傳來的數據有效。如果是get方法傳來的數據,還是會以默認的iso-8859-1來解碼。
    request.setCharacterEncoding("UTF-8");

4. 設置tomcat服務器配置文件server.xml,指定以何種編碼解碼瀏覽器傳來的參數。

    <Connector port="8080" protocol="HTTP/1.1"
               connectionTimeout="20000"
               redirectPort="8443" URIEncoding="UTF-8" />

‘URIEncoding="UTF-8"‘, 這個屬性的配置, 和"request.setCharacterEncoding("UTF-8");"這句代碼的功能大致相同。都是指定tomcat服務器接受到收據後如何解碼。

如果不指定,tomcat服務器會默認使用iso-8859-1來解碼。

被寫入文件:

1. 文件內容在被寫入文件時是被編碼了的,默認的編碼格式是gb2312(可能和系統有關,簡體中文系統測試,就是gb2312)。

也可以在代碼中指定文件流在寫入文件時用什麽編碼格式。比如指定用"utf-8".

Writer write = new OutputStreamWriter(new FileOutputStream(file), "UTF-8");

2. 文件本身不同軟件是有選擇性的支持可以對哪些編碼進行解碼。

比如:

Excel支持gb2312,不支持UTF-8。

Txt記事本支持UTF-8編碼。

所以有時候,我下載一個csv文件(指定內容使用utf-8編碼)。會遇到這樣的情況:

這個csv文件在使用excel打開的時候,中文是亂碼的,如果換成txt打開,中文就正常顯示了。

這個時候如果想要在excel中不亂碼,就需要在代碼中指定內容使用gb2312編碼。

也可以通過txt的‘另存為‘,改變文本的編碼格式。

3. 如何確定文件的編碼呢?

我在win7(簡體中文)系統中,右鍵->新建一個文本文檔,nodepad++打開後,查看編碼,可以看到編碼是ANSI。

使用java代碼生成一個txt文件,未明確指定編碼格式,nodepad++打開後,查看編碼,可以看到編碼是utf-8。

做了一個小測試:

test1. 指定utf-8編碼格式

        String fullPath = "D:\\test2.txt";
        File file = new File(fullPath);
        if (!file.exists()) {
            file.createNewFile();
        }
        Writer write = new OutputStreamWriter(new FileOutputStream(file),
                "utf-8");
        write.write("你好");
        write.flush();
        write.close();

生成的文件編碼格式是:

技術分享

test2. 指定gb2312編碼格式

        String fullPath = "D:\\test2.txt";
        File file = new File(fullPath);
        if (!file.exists()) {
            file.createNewFile();
        }
        Writer write = new OutputStreamWriter(new FileOutputStream(file),
                "gb2312");
        write.write("你好");
        write.flush();
        write.close();

生成的文件編碼格式是:

技術分享

test3. 先使用utf-8生成文件,輸出"你好",再使用gb2312,追加內容,輸出"你好",結果是這樣子的。

技術分享

總結:

第一次使用utf-8編碼格式輸出你好,此時文件內容 第一個"你好" 是utf-8編碼的,文件的解碼方式也是utf-8.

第二次使用gb2312編碼格式輸出你好,此時的文件追加的內容 第二個"你好" 是gb2312編碼的,文件的解碼方式也變成了gb2312。

也就是說,txt文件會一直使用最後一次操作文件使用的編碼格式來解碼文件的內容。

所以第二次輸出"你好"後,第一次輸出的"你好"變成了亂碼。

java中的編碼問題