Posted on 2008-10-08 15:07
英雄 閱讀(2477)
評論(0) 編輯 收藏
真是一個老生常談的問題啦!
項目上遇到啦,環境是:
1.中間件和數據庫都各自在hp-unix機器上。數據庫oracle字符集采用ZHS16GBK;中間件(基于jdk1.5)啟動時指定字符集是#export lang=zh_CN.hp15CN。
2.客戶端采用applet實現。
情況就是有個人名“李袆”,通過客戶端輸入保存后,再查詢出來就變成了“李?”。
好啦,重新復習一下java亂碼問題,google了一大把,有的說得都不對,現在先推薦兩篇權威的。
http://java.sun.com/developer/technicalArticles/Intl/HTTPCharset/;
http://java.sun.com/developer/technicalArticles/Intl/Supplementary/;
開始解決問題啦。
1.jvm在內存里存字符串是以utf-16存的,但是class文件和序列化對象文件里的字符串內容以“modified”utf-8存;
在jvm1.5里char代表的是編碼unit,不是編碼point。這樣就有可能兩個char才代表一個字符。這個“代表”的變化是為適應unicode從1.0升到2.0版本,老jdk到新jdk的一個概念改變,很多api和jdk工具因此也發生變化。那時unicode1.0里就是定長16位代表字符,所以老jdk里一個char就代表一個point,當然也是一個unit。但是unicode2.0擴展開來了,因此為了兼容,新jdk就確定一個char只代表一個unit,表示一個point得用int。
“modified”utf-8是java對標準utf-8編碼的一點更改,可以認為是種特殊編碼,為了適應java需要而存在的字符集。
2.客戶端接受輸入法輸入后,在客戶端jvm里以utf-16存“袆”,而后發送給中間件是通過序列化,以"modifed"utf-8傳送,中間件反序列化就又在中間件jvm里以utf-16存“袆”啦。這時中間件里的那個java對象要通過 jdbc給oracle發送過去此字符串,oracle的jdbc驅動將以系統默認字符集進行編碼傳送??墒莦h_CN.hp15CN是gb2312標準,沒有這個生僻字,只好搞一個“不存在編碼”編碼,這一編碼就把信息丟失了;而數據庫是以ZHS16GBK認識接受到數據,將這個“損壞的字”存到數據文件里啦。這里請問下同行讀者,oracle數據庫端會不會做zh_CN.hp15CN到ZHS16GBK的轉碼還是直接認為傳過來的一定是ZHS16GBK直接存到數據文件?我個人認為數據庫不做這種轉碼,浪費性能么。但不管怎么說,在中間件哪里,“袆”字就被“損壞”了,傳到數據庫怎么樣也無法恢復了。
3.這樣客戶端再去獲取時肯定是顯示不出來“袆”字啦。
于是讓客戶執行#export lang=zh_CN.gb18030再啟動中間件,問題解決。
補充:
1.oracle的jdbc thin驅動就是這樣,使用系統默認字符集進行傳送編碼,不像mysql可以在連接url中指定。不過我個人認為這也不錯,因為你即使在url指定正確的編碼格式,還要保證操作系統裝著這個字符集呢。
2.jsp
%@page contentType="text/html;charset=gb2312" language="java" pageEncoding="gb2312"%這個標記分兩部分,pageEncoding是指jsp文本文件本身存在硬盤的編碼格式,servlet容器按這個編碼格式讀取,從而再做轉碼到合適編碼(一般utf-8)的java文件,再編譯成含“modified utf-8字符串”的class文件,再加載成實例對象;當該對象吐出response時,以contentType里的charset進行編碼在網上傳送給客戶端,并置response頭信息編碼格式。
3.瀏覽器客戶端接受到response時,會根據頭信息獲取編碼格式,從而解析出正確字符,再根據html規范進行顯示。而對于網頁提交表單時,也會根據這個信息進行表單內容數據的編碼。編碼后再進行url編碼即發送給服務器??墒潜韱屋斎胗锌赡艹鲞@個字符集,則多數瀏覽器會采用(unicode point number)來編碼這個字符,繼續發送給服務器。同樣,如果瀏覽器在接受response時,得到這種形式的字符串,多數也將進行解碼從而顯示出來。
4.多數servlet容器在解析request獲取參數時統一采用默認配置的iso-8859-1。這個字符集只包含西歐字符,因此解析前統一setCharacterEncoding是必須的。 當然多數都也可以修改默認配置啦。