編碼

一:java 編碼
java 數據類型
1.整型
Java定義了4個整型類型:byte、short、int、long,且它們都是有符號的正負值。java不支持無符號的正整數。下表列出了這4中類型的編碼方式、數據寬度和其表示範圍。
數據類型       編碼方式        所佔位數        範圍                   說明
byte            補碼              8                  -128~127        最小的整數類型是字節(byte),有符號8    

                                                                                   位 類型。
short          補碼              16                -32768~32767
int              補碼               32                -2147483648~2147483647  long 補碼 64
2.字符
與C/C++不同,Java語言使用Unicode字符集來定義字符類型,即使用16位無符號整數來表示一個字符。Unicode字符集定義了人類語言中出現的

所有字符,是很多字符集的集合。在Java中,char的範圍是從0~65536 ,而我們熟悉的ASCII標準字符集的範圍是從0~127,擴展的8位字符集也只是0~255。

     總結一些自己關於編碼方面的心得,彙集網上衆多關於此的帖子資料,已被日後查詢只用。
首先好像Jole講過這樣一句話,大意是:當面對一串字節流的時候,如果不指定它的編碼,其實際意義是無法知道的。
這句話應該也是我們面對"字符轉字節,字節轉字符"問題時,時刻應牢記的。否則亂碼問題可能就接踵而至。
其實亂碼問題的本質就是Encoding和Decoding用的不是一個編碼,明白了這個道理就很好解決亂碼問題了。
  Java中常見的時候有如下:
    1. String類使用byte[]的構造函數 String(byte[] bytes),String類同時提供了兩個重載
        String(byte[] bytes, Charset charset)
        String(byte[] bytes, String charsetName)
    通過使用指定的 charset 解碼指定的 byte 數組,構造一個新的 String。
    2. String類的getBytes函數 byte[] getBytes() 同樣有如下兩個重載:
         byte[] getBytes(Charset charset)

         byte[] getBytes(String charsetName)

    使用給定的 charset 將此 String 編碼到 byte 序列,並將結果存儲到新的 byte 數組。
    3. PrintStream的 print(String s)同樣設計到這個問題,爲此PrintStream的構造函數中除了PrintStream(File file),還有PrintStream(File file, String csn) 否則the string's characters are converted into bytes according to the platform's default character encoding, DataOutputStream構造時沒有方法指定編碼,但其提供了一個writeUTF(String str)未指定編碼的都是使用the platform's default charset, 可使用System.getProperty("file.encoding"),Charset.defaultCharset()獲得。但經我測試都是依賴於你運行此代碼的Java源文件編碼,如Java源文件編碼爲UTF-8 ,則 System.getProperty("file.encoding") 輸出爲UTF-8 ,而如果爲GB2312,則輸出爲

GB2312。
一個問題,java的文件編碼是什麼?生成的class文件編碼是什麼,探討一下下面這個例子:
例如:
有java 源文件:
public class EncodingTest {
    public static void main(String[] args) {
        String s = "哈";
        String des = "";
        try {
            System.out.println("默認編碼:"+System.getProperty("file.encoding"));
            des = new String(s.getBytes(), "GB2312");
            System.out.println("String : GB2312 =" + des);
            String s_string1 = new String(s.getBytes(), "UTF-8");
            System.out.println("String : UTF-8 =" + s_string1);
        } catch (Exception ex) { }
     }
}
如果EncodingTest.java的編碼是GB2312,則輸出結果是:
默認編碼:GB2312
String : GB2312 =哈
String : UTF-8 =
如果EncodingTest.java的編碼是UTF-8,則輸出結果是:
默認編碼:UTF-8 
String : GB2312 =?
String : UTF-8 =哈
      
解釋:
首先java源文件是按照默認編碼(而它的默認編碼就是保存時使用的編碼,我認爲是這樣的。)以byte stream的方式存儲在硬盤上的。
例:
如果Editor(編輯器)把*.java存成GBK或GB2312文件編碼,這個"哈"存在硬盤上的byte stream是兩個byte,B9 FE,(查詢GBK編碼表知道 "哈"的十六進制編碼表示正是B9 FE)。如果Editor把java存成UTF-8文件編碼,這個"哈"存在硬盤上的byte stream是兩個byte,54 C8,也就是以Unicode碼存儲。(順便提一下,"哈" 的Unicode碼是54 C8,UTF-8 是E5 93 88)。所有unicode char 都是兩個字節(也有四字節的,Unicode 3.1增加了一個B擴展區,編碼進入四字節。編碼空間約爲21

從 hē 到 xiāo ,共增加了42711個四字節東亞表意文字目前計算機中實際可使用70269個東亞表意文字(27558+42711)。Java string 在內存裏不是按UTF-8編碼的,只有生成了class文件,代碼裏的string literal纔是按UTF-8編碼存的。由於UTF-8是變長的,所以那個字可能是1 byte, 2 bytes 或3 bytes。


關於UTF-8編碼的。
http://forum.javaeye.com/viewtopic.php?t=21684


下面是瞭解編碼和字節碼的關係。
byte[] bytes = "中".getBytes("GBK");
按照GBK編碼表中字的編碼爲 D6 DO,用上面方法取得字節數組時,它的值爲 bytes[0] = -42,bytes[1] = -48 。這兩個值和GBK編碼表的D6 D0
有什麼關係?答案是:我們說過byte的取值範圍是-128 ~ 127,大於127的數值都會變爲負數。如果是負數就+256變成正確的值。再將這個值轉換成16進制,就是上面的D6 D0了。
在程序員觀點下,有字符和字節兩個概念,但它們之間並沒有直接的對應關係,只有在指定了一種特定的編碼方式(比如GB2312)後,纔會有一定的對應關係。舉例來說,一箇中文字符,在GB2312的編碼方式下對應兩個字節,在UTF-8編碼方式下對應三個字節,而在UCS2編碼方式下對應兩個 字節;而對於標準 ASCII 字符來說,在GB2312和UTF-8 編碼方式下對應一個字節,在UCS2編碼方式下對應兩個字節。
有如有源碼片段:
byte[]   b   =   new   byte[]{-128, 0, -64, 0, -32, 0}; 
String   s   =   new   String(b); 
字符串的編碼、解碼過程不是完全可逆的。不同的編碼方案都有數據轉換時的盲區。
對於你舉的例子來說,由於GBK編碼只對連續的兩個小於0的字節才識別爲漢字,
所以,類似-128,0這樣的字節序列,對於GBK是不可識別的,轉換後爲字符'?',結果也就是63 ,把-128換成其他的負數,結果也是一樣的。要保持原字節的內容,要採用“ISO8859_1”。
修改後的代碼如下: 
  byte[]   b   =   new   byte[]   { -128,   0,   -64,   0,   -32,   0  };
  String   s   =   new   String(b,"ISO8859_1");
  byte[]   c   =   s.getBytes("ISO8859_1");
  for   (int   i   =   0,   j   =   b.length;   i   <   j;   i++)   { 
          System.out.println(b[i]   +   "/t"   +   c[i]);
  }
這也就:如果EncodingTest.java的編碼是GB2312,則輸出結果是:String : GB2312 =哈:s.getBytes()通過使用默認的GB2312(因爲 EncodingTest.java的編碼是GB2312) 解碼指定的 byte 數組爲:bytes[-71,-2],-71 + 256 = 185(十進制),對應B9(十六進制),-2 + 256 = 254(十進制),對應FE(十六進制)。
                          
生成的class文件編碼是什麼?
                        
javac首先看有沒有-encoding這個開關,如果有,就按這個開關指定的encoding去解碼,如果沒有,就按系統省卻的encoding去解碼,
這個系統encoding設置在file.encoding這個property裏面。
Java是使用Unicode字符集的,意思是在內存運行的時候是這樣的。當Java源程序被編譯爲.class文件的時候,是以UTF-8字符集存儲字符的。
比如字符串"漢語"的Unicode字符爲:0x49 6C ED 8B。在.class文件中則變成了:0xE6 B1 89 E8 AF AD。實際上如果用以下程序輸出 
"漢語"二個字的16進制字節序列:
        String str = "漢語"; 
        try{byte[] b = str.getBytes("Unicode");
        for(int i : b){   
         System.out.println(Integer.toHexString(i));
        } 
        }catch(Exception ex){ex.printStackTrace();  }
會發現,實際輸出的是 0xFE FF 6C 49 8B ED。多出來的0xFE FF,是Unicode字符串的BOM(Byte Order Mark)。但是爲什麼輸出的順序 
(0xFE FF 6C 49 8B ED)和文件中的順序不一致(0xFF FE 49 6C ED 8B)?
public byte[] getBytes(Charset charset) 使用指定的字符集將此 String 解碼爲字節序列,並將結果存儲到一個新的字節數組中。
如果不指定參數,則使用系統默認的字符集,對於簡體中文系統一般爲GBK。
                                        
有一servlet UnitePlanServlet.java
public class UnitePlanServlet extends  HttpServlet {
    static final private String CONTENT_TYPE = "text/html; charset=GBK";
    public void doGet(HttpServletRequest req, HttpServletResponse resp)
                                                        throws ServletException, IOException {
          doPost(req, resp); 
    } 
    public void doPost(HttpServletRequest req, HttpServletResponse resp)
                                                        throws ServletException, IOException {
         doAction(req, resp);
    } 
    private void doAction(HttpServletRequest req, HttpServletResponse resp) 
                                                        throws ServletException, IOException {
          try {
               String st = "哈";
               System.out.println("默認編碼:"+System.getProperty("file.encoding")); 
               String des=new String(st.getBytes(),"GB2312");
               System.out.println("String : GB2312 =" +des); 
                                            
               String s_string1 = new String(st.getBytes(),"UTF-8"); 
               System.out.println("String : UTF-8 =" +s_string1); 
                                                          
               RequestDispatcher rd = getServletContext().getRequestDispatcher("/myplan.jsp");
              rd.forward(req, resp);  
                     
           } catch (Throwable e) {}            
    }
}
web.xml 片段:
<servlet>   
<servlet-name>UnitePlanServlet</servlet-name>
<display-name>UnitePlanServlet</display-name>
<description></description>
<servlet-class>servlet.UnitePlanServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>UnitePlanServlet</servlet-name>
<url-pattern>/servlet/unitePlan</url-pattern>
</servlet-mapping>

一個jsp myplan.jsp
<%@ page contentType="text/html; charset=GBK"%>
<html>
<head>
<title>無標題文檔</title>
<script>
function mergeItem(){
  window.location.href="<%=request.getContextPath()%>/servlet/unitePlan";
}
</script> 
</head> 
<body>
<input οnclick="mergeItem();"  type="button" value="查看" name="noregis6" />  
</body>
</html>           
點擊"查看"按鈕調用servlet UnitePlanServlet.java ,
如果UnitePlanServlet.java 編碼是GB2312,輸出結果:
默認編碼:UTF-8

String : GB2312 =?
String : UTF-8 =哈
如果UnitePlanServlet.java 編碼是UTF-8,輸出結果:

默認編碼:UTF-8
String : GB2312 =?
String : UTF-8 =哈
也就是和源文件UnitePlanServlet.java的編碼沒關係。因爲jsp實際調用的是UnitePlanServlet.class,上面我們說過當Java源程序被編譯爲.class文件的時候,是以UTF-8字符集存儲字符的,也就是*.class(字節碼文件)的編碼方式是UTF-8。

二:jsp編碼 

(一)jsp中影響編碼的屬性及其設置小結(contentType,pageEncoding,charset,setCharacterEncoding)2008-03-19 22:451. 名詞解釋及其作用
    1. contentType: <%@ page contentType="text/html; charset=UTF-8"%>
    2. pageEncoding:<%@ page pageEncoding="UTF-8"%>
    3. html頁面charset:<META http-equiv="Content-Type" content="text/html; charset=UTF-8">
    4. setCharacterEncoding:request.setCharacterEncoding(),response.setCharacterEncoding()
    5. setContentType:response.setContentType()
    6. setHeader: response.setHeader()
    7. jsp頁面編碼: jsp文件本身的編碼
    8. web頁面顯示編碼:jsp的輸出流在瀏覽器中顯示的編碼
    9. web頁面輸入編碼: 輸入框輸入的字體編碼
    10. web服務器輸入的請求流: web Server相應瀏覽器的請求數據
    11. web服務器輸出的響應流: web Server相應瀏覽器的輸出數據
2. 他們之間的相互影響和作用域,以及先後作用順序
    1. pageEncoding: 只是指明瞭 JSP 頁面本身的編碼格式,跟頁面顯示的編碼沒有關係;
    容器在讀取(文件)或者(數據庫)或者(字符串常量)時將起轉化爲內部使用的 Unicode,而頁面顯示的時候將內部的Unicode轉換爲contentType指定的編碼後顯示頁面內容;
    如果pageEncoding屬性存在,那麼JSP頁面的字符編碼方式就由pageEncoding決定,否則就由contentType屬性中的charset決定,如果charset也不存在,JSP頁面的字符編碼方式就採用默認的ISO-8859-1。
    2. contentType: 指定了MIME類型和JSP頁面迴應時的字符編碼方式。MIME類型的默認值是“text/html”;字符編碼方式的默認值是“ISO-8859-1”. MIME類型和字符編碼方式由分號隔開;
    3. pageEncoding和contentType的關係:
        1. pageEncoding的內容只是用於jsp輸出時的編碼,不會作爲header發出去的; 是告訴web Server,jsp頁面按照什麼編碼輸出,即web服務器輸出的響應流的編碼;
       2. 第一階段是jsp編譯成.java,它會根據pageEncoding的設定讀取jsp,結果是由指定的編碼方案翻譯成統一的UTF-8 JAVA源碼(即.java).無論pageEncoding指定是
UTF-8,還是GB2312,最終翻譯成德java源碼的編碼方式都是UTF-8。
       3. 第二階段是由JAVAC的JAVA源碼至java byteCode的編譯,不論JSP編寫時候用的是什麼編碼方案,經過這個階段的結果全部是UTF-8的encoding的java源碼.JAVAC用
UTF-8的encoding讀取java源碼,編譯成UTF-8 encoding的二進制碼(即.class),這是JVM對常數字串在二進制碼(java encoding)內表達的規範.
       4. 第三階段是Tomcat(或其的application container)載入和執行 “階段二” 來的JAVA二進制碼,輸出的結果,也就是在客戶端見到的,這時隱藏在階段一和階段
二的參數contentType就發揮了功效  
    4. 和contentType效果一樣的設置方式還有 html頁面charset, response.setCharacterEncoding(),response.setContentType(),response.setHeader();
response.setContentType(),response.setHeader();優先級最好,其次是response.setCharacterEncoding();再者是<%@page contentType="text/html; chareset=gbk"%>,
最後是<meta http-equiv="content-type"content="text/html; charset=gb2312" />.
    5. web頁面輸入編碼: 在設置頁面編碼<%@page contentType="text/html; chareset=gbk"%>的同時,也就指定了頁面的輸入編碼;如果頁面的顯示被設置爲UTF-8,
那麼用戶所有的頁面輸入都會按照 UTF-8 編碼; 服務器端程序在讀取表單輸入之前要設定輸入編碼;表單被提交後,瀏覽器會將表單字段值轉換爲指定字符集對應的字節值,然後根據 HTTP 標準 URL編碼方案對結果字節進行編碼.但是頁面需要告訴服務器當前頁面的編碼方式;request.setCharacterEncoding(),能修改Serverlet獲取請求的編碼,response.setCharacterEncoding(),能修改Serverlet返回結果的編碼。

 

(二)jsp中文問題 QA (回答的可能有些不準確) -------網上帖子收集。

1 第一步,程序員用編輯工具編寫jsp文件,然後保存。此時如果不特別指定,一般都是以平臺的默認編碼保存的。比如在中文win2k上,這個jsp文件是以GBK編碼的。   

回答:jsp文件就是文本文件,保存得編碼與你使用得編輯器相關,在jbuilder裏邊如果你把系統編碼設置爲gbk,jsp默認會用gbk保存。而在eclipse裏邊,則是按照jsp
規範中指定得,如果頁面沒有指定編碼規則,則頁面是iso8859-1,jsp文件就是文本文件,保存得編碼與你使用得編輯器相關,在jbuilder 裏邊如果你把系統編碼設置爲
gbk,jsp默認會用gbk保存。 而在eclipse裏邊,則是按照jsp規範中指定得,如果頁面沒有指定編碼規則, 則頁面是iso8859-1,所以一個含有中文得文件,如果未指定
編碼,eclipse 保存後漢字全部變成問號。當然也可以手工指定每個文件的編碼。
2 . web容器將jsp文件編譯爲servlet class文件。編譯器需要讀取硬盤上的jsp文件,那麼它以什麼字符集來解碼呢?這個過程就像我們編輯一個文本文件,然後保存(是GBK編碼),你再用編輯器打開時,編輯器會以GBK來解碼一樣。不過這裏還是複雜一點,jsp編譯器根據什麼來確定解碼字符集?是< %@page pageEncoding="GB2312"% >麼?那麼當2個頁面指定的 pageEncoding不同並且用include將其合在一起時,jsp編譯器的策略是什麼?


回答:

       web容器首先將jsp頁面轉換成servlet的java文件,對於每個jsp頁面,按照jsp的規範,其編碼的確定有大概4個步驟,指定了pageEncoding的最優先,還可以在配
置文件中指定編碼,很多種方式,我覺得指定pageEncoding最方便。如果不指定,默認的是iso8859-1而不是系統的默認編碼。jsp規範是這樣規定的,但是不同的web容
器對於規範的實現上稍有差別。但是一般來說,指定了pageEncoding之後,不會出現問題。web容器轉換得到的java文件的編碼——只有一個,UTF-8。

3. 瀏覽器以get或post方法傳遞參數時,是以什麼編碼的?是否UTF-8?以get傳遞時還要經過urlencoding,除此之外和post方法還有何不同?看車東的文章
http://www.chedong.com/tech/hello_unicode.html,get傳遞時,是先按GBK編碼再urlencoding,我自己試驗的結果也是如此。那麼設定瀏覽器以UTF-8發送還有什麼作
用?另外,如果是這樣,那麼web容器怎麼知道按什麼來解碼?因爲客戶端可是多種多樣阿,傳過來的參數什麼編碼的都有,web容器怎麼處理?

 

4. web容器得到瀏覽器傳遞的參數,以什麼字符集解碼?若瀏覽器以UTF-8編碼,這裏只能是UTF-8,如果瀏覽器以平臺字符集編碼,那麼這裏web容器是怎樣成功解碼的
呢?成功解碼後,將參數寫進request對象裏時又是什麼編碼?默認ISO-8859-1?從request裏取得的參數是以什麼來解碼?通過request.setCharacterEncoding("GB2312")
指定?

3和4 的回答:

        對於get/post的編碼問題,這應該是http協議中的內容吧,研究的不多。說說自己的經驗。對於post,發送和服務器端處理都是按照html的編碼來進行的。所以gbk的頁面
發送過去,用request.setCharacterEncoding("GBK")即可得到正確的內容。但是注意,這個函數的調用必須在所有 request.getParameter的調用之前纔有效。 對於get,
瀏覽器發送的時候,對字符應該是按照html頁面的編碼進行url 的編碼。而在服務器端,我沒有研究jsp規範中是否有規定,只知道在tomcat 中,如果不進行配置,那麼
url編碼的默認編碼規則tomcat會認爲是 iso8859-1,但是實際發送過來的是html頁面中的編碼的urlencoding,所以會導致亂碼。tomcat中可以設置
useBodyEncodingForURI="true"來 解決這個問題。我記得有另一個屬性可以指定url的編碼,是URIEncoding吧。召喚達人對get進行詳細介紹。

5. servlet從request裏取得信息,並進行一些操作後,往response的輸出流裏寫入信息時又是什麼編碼?web容器從response輸出流裏讀取時怎樣解碼?然後以什麼字符
集編碼發送到客戶端瀏覽器?默認ISO-8859-1?通過response.setContentType("text/html;charset=GB2312")指定?當2個頁面通過include合在一起怎麼辦?

 

回答:

       一個頁面(包括其include 的文件),其contentType只能被指定一次 (用<  %@page .. %  >指定),但是有些服務器檢查不嚴格,可能可以指定多次。其輸出的編碼當然
是這個指定的編碼了。對於包含,只需要在頁首加上pageEncoding即可,參考2。

可能有疏漏或錯誤,大家多指點或補充。注:提到jsp規範,都是指jsp2.0規範。可以從sun的網站下載。

 

6. 瀏覽器根據本地編碼將信息發送給服務器端,服務器端怎樣解碼?是讀取http頭信息accept language麼?  成功解碼後,構造request對象時,用什麼編碼?包括再從
request裏取信息。 我的想法是通過request.setCharacterEncoding("GB2312")指定,response同此理。我想web容器總要經過解碼-編碼-構造request對象的處理過程,不
知道對不對。   

 

回答:

       html頁面的meta信息中有頁面的編碼信息 request對象的解碼取決request.setCharacterEncoding,所以web容器並不需要進行解碼,解碼可以在讀取request中內容的時候再解,response的解碼過程,就是一個字符流的寫入過程,寫入的字符流是什麼編碼決定contentType中的charset 所以,web容器對頁面內容不需要做編碼、解碼的工作。要說編碼解碼也就是對url請求進行解碼而已 。web容器一開始從客戶端取得的一定是經過編碼的字節流對不對?然後不管怎樣,它要把這些信息放進request對象。再然後,通過request. getParameter()取得信息。 web容器取得字節流後,根本不進行解碼,直到request. getParameter()時才根據request.setCharacterEncoding()的設置進行解碼在這時才完成字節流到java String的轉換,這時又要分時get請求,還是post請求setCharacterEncoding()用來設置http請求或者相應的編碼。對於request,是指提交內容的編碼,指定後可以通過getParameter()則直接獲得正確的字符串,如果不指定,則默認使用iso8859-1編碼,需要進一步處理。參見下述 "表單輸入"。值得注意的是在執行setCharacterEncoding()之前,不能執行任何getParameter()。java doc上說明:This method must be called prior to reading request parameters
or reading input using getReader()。而且,該指定只對POST方法有效,對GET方法無效。分析原因,應該是在執行第一個getParameter()的時候, java將會按照編碼分析所有的提交內容,而後續的getParameter()不再進行分析,所以setCharacterEncoding()無效。而對於GET方法提交表單是,提交的內容在URL中,一開始就已經按照編碼分析所有的提交內容,setCharacterEncoding()自然就無效。對於response,則是指定輸出內容的編碼,同時,該設置會傳遞給瀏覽器,告訴瀏覽器輸出內容所採用的編碼。 (更多內容見java encoding參考 )不知道你用的是不是tomcat容器,如果是的話,就如以下:

我說的問題是爲什麼get只能用用字節轉換處理。這是因爲tomcat4.x和tomcat5.x不太一樣了。如果你用4.x的話,應該不會出現這樣的問題。因爲到了tomcat5.x後,它把get和post提交分開來處理了。post和原來是一樣的所以用Filter類就可以了。但是get的方式必須用String des=new String(s.getBytes("iso8859-1"),"GBK。而且字符集也必須是iso8859-1,因爲get默認的字符集是這樣的。 (Jsp頁面URL中傳遞參數是一種GET請求,如超鏈接。Form表單提交時POST請求。)還有一種辦法就改tomcat的server.xml文件,如下:

Java代碼

< Connector port="80"   maxThreads="150"   minSpareThreads="25"   maxSpareThreads="75"   enableLookups ="false"  redirectPort="8443"  acceptCount="100" debug="0"  connectionTimeout="20000" disableUploadTimeout ="true"   URIEncoding ="GBK"  />  

修改後只要字符編碼都設成統一的話,就不用轉了。

而我原來想的是:web容器取得字節流後,馬上進行解碼,轉成某種編碼的字符流,再將此字符流轉成字節流寫進request對象中。這樣request. getParameter()時當然就又要解碼了,原來不是這樣的。

 

舉個例子說明指定編碼的必要:
     如果一個網頁指定編碼爲utf-8, <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />, 頁面上有一個form,提交到一個servlet,那麼用戶
輸入的字符傳過來的字節流就是按指定編碼encoding的,例如你輸入了"Hello你好",如果是utf-8,那麼傳過來的就是如下:

         [104, 101, 108, 108, 111, -28, -67, -96, -27, -91, -67]
 我們看到後面漢字每個用了3個字節,這個可以參考Utf-8的相關知識。但如果你頁面指定的是GBK,那傳過來的就不一樣了:
 [104, 101, 108, 108, 111, -60, -29, -70, -61]
所以servlet端,當使用request.getParameter的時候內部應該是調用String s = new String(bytes, response.getEncoding())的,如果你的response沒有設置編碼,
那麼就採用默認的編碼(tomcat是ISO-8859-1),不會會轉爲java 平臺的GBK,那中文就變成亂碼了。 所以爲了避免亂碼,jsp站點一般設一個過濾器,所有的頁面、servet都設置統一的編碼。設置 response.setEncoding, request.setEncoding。

Java的String內部是一個char[], char是一個用16位存儲的utf-16編碼的單元。爲此,當要把字符、字符串轉爲字節輸出到文件、網絡,或者從文件、網絡讀到的字節流還原爲有實際意義的字符,都要明白其編碼是什麼。討論JSP轉爲servlet的問題,不要把這個問題和瀏覽器與Servlet Container之間通訊時的亂碼問題混在一起. 我把討論侷限於Tomcat 5.5, 發佈模式是jsp文件寫好了之後直接放在Tomcat裏,瀏覽器在向Tomcat發requests時, Tomcat把jsp轉化成java文件,再把java文件編譯成class文件。沒有用事先把jsp編譯成class文件的那種發佈方式。

 

    有三個文件,1. jsp 2. 生成的java文件  3. 生成的class文件。 有兩個步驟,1. 把jsp轉化成java文件, 2.把java文件編譯成class文件.

注意Tomcat 5.5用Eclipse JDT Java Compiler來編譯JSP文件,而不用JDK. 參見http://tomcat.apache.org/tomcat-5.5-doc/RELEASE-NOTES.txt

第一個問題,jsp的文件編碼是什麼?爲什麼這個問題對JSP編譯成servlet有影響,探討一下下面這個例子:

Java代碼

<% String s = "哈"; %>  <h1><%=s%></h1> <% String s = "哈"; %><h1><%=s%></h1>

如果editor把jsp存成GBK文件編碼,這個"哈"存在硬盤上的byte stream是兩個byte, B9 FE,如果editor把jsp存成Uft-8文件編碼,這個"哈"存在硬盤上的byte stream是三個byte,E5 93 88.(順便提一下,“哈”的Unicode碼是54 C8。)

這兒editor是起決定作用的,一定要搞清楚,editor存成文件用的編碼是什麼。對pageEncoding的設置一定要和存成的文件編碼一至。 如果editor只能存成GBK,而pageEncoding錯設置成UTF-8,這兩個就不一至了。我見過很多人寫XML文件時,錯以爲把最開頭的 eccoding改成UTF-8就行了,結果editor不支持UTF-8,於是出錯。

下一個步驟是Tomcate把jsp轉化成java文件,Tomcate需要知道jsp文件的編碼,要不碰上E5 93 88這三個byte,是把它們當 成UTF-8編成"哈"呢,還是把它們當成Latin-1(也就是ISO-8859-1)編成三個別的什麼鬼符號呢? 根據Java  Server  Pages那本書的說法,jsp編譯器先看pageEncoding,再看charset,如果兩個都沒有,那麼如果jsp是用標準寫法<% %>,就按ISO-8859-1,那麼如果jsp是用XML語法寫的,就按UTF-8. 生成的java文件的編碼是什麼,<TOMCAT_HOME>/conf/web.xml
裏面有這麼一句javaEncoding,Java file encoding to use for generating java source files. [UTF8] ,我校驗了一下,是這樣的. 由java編成class容易了,由UTF-8到UTF-8。生成的class文件用的肯定是UTF-8,jvm規範規定好的。

問題的關鍵在於Tomcat把Jsp轉化成java文件是如何處理Jsp文件編碼的,再往下研究就得看JSP2.0 的Specification和Tomcat的源代碼。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章