2017年6月26日 星期一

Java 與 JSP 中文字形


Reference https://crluo0929.wordpress.com/

JSP 中文編碼問題

首先要先區別一下兩個術語的意義:
contentType : 指定的是 JSP 頁最終 Browser(客戶端) 所見到的網頁內容的編碼
就是瀏覽器上的的 encoding. 例如  JSPtw Forum 用的 contentType 就是  Big5
pageEncoding :  指定JSP 編寫時所用的編碼
這裡指的是在寫 JSP 時,是用什麼樣的編碼在撰寫,通常是 Big5 ,不過許多編輯器在存檔時也可以選擇檔案編碼。
JSP 在 compile 成 .class 時,會經過兩次的編碼,先是經過 pageEncoding 然後再來是轉成 UTF-8 的 java source code ,
最後跑出來的網頁用的是 contentType 。
所以若是 pageEncoding 選錯了,或沒設定( 預設 ISO8859-1 ) ,那在第一次編碼時中文就已經變亂碼了。
在 complie 成 bytecode 時,不管之前是用什麼編碼寫,在這個階段轉成的 java source code 都一定是 UTF-8 的編碼,
JAVAC 用 utf-8 的 ENCODING 讀取AVA原碼,編譯成字串是 utf-8 ENCODING 的 bytecode (.class)。
接著 Container 載入和執行 bytecode ,將執行結果傳給瀏覽器,而瀏覽器根據前面定義的 contentType 編碼讀顯示結果。
response 設定 ContentType 為 UTF-8 編碼
response.setContentType(“text/html; charset=utf-8″);
看到的 JSP 就是
<%@ page session="false" pageEncoding="big5″ contentType="text/html; charset=utf-8″ %>
也就是說,使用如上面這樣宣告 charset 為 UTF-8 編碼時,所有的字都會被編譯成 UTF-8 然後再轉換成 bytecode ,
若在這個頁面會送出 request 時,原本中文的參數也會被編譯成 UTF-8 送出,
因此在接收端的 jsp 也必須以 UTF-8 的方式編碼才能正常顯示。
接收端的 jsp 在接收參數時,因 Container 在傳遞參數時預設使用 ISO-8859-1 的方式,因此在接收端必須先以
ISO-8859-1 的方式解譯,然後再轉換成由傳送端傳過來的編碼 ( UTF-8 ) 。
如下面例子:
傳送端:
<%@ page language="java" contentType="text/html; charset=UTF-8″ %>
<a href="/Web/jsp/receive.jsp?name=小明>GET</a>
如此,傳送端會將 “小明" 以 UTF-8 編碼起來,並透過 Container 以 ISO-8859-1 的方式傳遞給接收端
接收端:
String name = request.getParameter( “name" ); //此時 name 為 ISO-8859-1
name = new String( name.getBytes( “8859_1″ ) ,  “UTF-8″ ) ;
上面這行轉換是因為傳送端把 “小明" 以 UTF-8 編碼,因此要以同樣的方式才能正常顯示
接收端另外一種寫法:
request.setCharacterEncoding( “UTF-8″ ) ; //預設會將所有參數的編碼方式由 ISO-8859-1 轉換成 UTF-8
String name = request.getParameter( “name" ); //此時,取到的 name 就已經是 UTF-8 編碼
再另外一種寫法:
如果在每個頁面都要加上 request.setCharacterEncoding() 這樣實在是不好維護,因此可以在 web.xml 裡加個 Filter ,
<filter>
<filter-name>encoding</filter-name>
<filter-class>MyFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>Big5</param-value>
</init-param>
</filter>
MyFilter 實作 Filter 在 init() 裡透過 FilterConfig 取得設定參數 Big5
在 doFilter 裡去設定 request.setCharacterEncoding( 取得的設定參數 ) 即可。
另外一種例子:
傳送端:
<%@ page language="java" contentType="text/html; charset=Big5″ %>
<a href="/Web/jsp/receive.jsp?name=小明&sex=<%= java.net.URLEncoder.encode( “男"  , “UTF-8″ ) %> >GET</a>
此時,在送出時, “小明" 會依據 charset 編碼被編譯成 Big5 ,但性別 “男"  會被指定以 UTF-8 編譯
接收端:
String name = request.getParameter( “name" ); //此時 name 為 ISO-8859-1
String sex = request.getParameter( “sex" ) ; //此時 sex 為 ISO-8859-1
name = new String( name.getBytes( “8859_1″ ) , “Big5″ ) ;
sex = new String( sex.getBytes( “8859_1″ ) , “UTF-8″ ) ;
因 name 在傳送端被編譯成 Big5 因此接收端須要以 Big5 轉譯才能正常顯示,
而 sex 在傳送端被 java.net.URLEncoder 指明要以 UTF-8 編碼,因此在接收端也必須以 UTF-8 轉譯才能正常顯示
若為此種例子,則無法使用
request.setCharacterEncoding( “Big5″ ) ;
因為若這樣使用,則接收端在接收參數時,所有的參數都會被以 Big5 轉譯,因此即使寫了
sex = new String( sex.getBytes( “8859_1″ ) , “UTF-8″ ) ;
此時的 sex.getBytes( “8859_1″ ) 的 sex 其實已經先被轉譯成 Big5 了,因此用 8859_1 轉譯出來就會是亂碼,
再轉換成 UTF-8 時,還是亂碼。
※ pageEncoding 和contentType的預設都是 ISO8859-1,而隨便設定了其中一個,另一個就跟著一樣 ( 不一定絕對 )。



ISO
SO 8859-1,正式編號為ISO/IEC 8859-1:1998,又稱Latin-1或「西歐語言」,是國際標準化組織ISO/IEC 8859的第一個8位字符集。它以ASCII為基礎,在空置的0xA0-0xFF的範圍內,加入96個字母符號,藉以供使用附加符號拉丁字母語言使用。曾推出過 ISO 8859-1:1987 版。
英語雖然沒有重音字母,但仍會標明為ISO/IEC 8859-1編碼。除此之外,歐洲以外的部分語言,如南非荷蘭語斯瓦希里語印尼語馬來語、菲律賓他加洛語等也可使用ISO/IEC 8859-1編碼。

沒有留言:

張貼留言