java亂碼?問題

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

          只有注冊(cè)用戶登錄后才能發(fā)表評(píng)論。


          網(wǎng)站導(dǎo)航:
           
          主站蜘蛛池模板: 资阳市| 阳原县| 马边| 侯马市| 开原市| 隆昌县| 太谷县| 平顶山市| 岑溪市| 农安县| 左云县| 建瓯市| 德江县| 炉霍县| 阳曲县| 威信县| 衡东县| 民权县| 宁城县| 祁连县| 常山县| 财经| 宜春市| 柯坪县| 巴彦淖尔市| 印江| 清丰县| 罗甸县| 延吉市| 泸溪县| 琼结县| 印江| 斗六市| 怀来县| 台东市| 临沧市| 卫辉市| 龙南县| 常山县| 昌宁县| 巩义市|