Rookie

          Headache English

          <2008年1月>
          303112345
          6789101112
          13141516171819
          20212223242526
          272829303112
          3456789

          統計

          • 隨筆 - 25
          • 文章 - 1
          • 評論 - 15
          • 引用 - 0

          常用鏈接

          留言簿(2)

          隨筆分類

          隨筆檔案

          搜索

          •  

          積分與排名

          • 積分 - 29714
          • 排名 - 1400

          最新評論

          閱讀排行榜

          評論排行榜

          J2EE開發中字符處理
                  在java企業級開發中,會有多處涉及到字符集編碼,有些地方需要進行正確的設置,有些地方需要進行一定程度的處理。

          一,java中對字符的處理

                  getBytes(charset):這是java字符串處理的一個標準函數,其作用是將字符串所表示的字符按照charset編碼,并以字節方式表示。注意字符串在java內存中總是按unicode編碼存儲的。比如"中文",正常情況下(即沒有錯誤的時候)存儲為"4e2d 6587",如果charset為"gbk",則被編碼為"d6d0 cec4",然后返回字節"d6 d0 ce c4"。如果charset為"utf8"則最后是"e4 b8 ad e6 96 87"。如果是"iso8859-1",則由于無法編碼,最后返回 "3f 3f"(兩個問號)。
                  new String(charset)這是java字符串處理的另一個標準函數,和上一個函數的作用相反,將字節數組按照charset編碼進行組合識別,最后轉換為unicode存儲。參考上述getBytes的例子,"gbk" 和"utf8"都可以得出正確的結果"4e2d 6587",但iso8859-1最后變成了"003f 003f"(兩個問號)。因為utf8可以用來表示/編碼所有字符,所以new String( str.getBytes( "utf8" ), "utf8" ) == str,即完全可逆。
                 
          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,則是指定輸出內容的編碼,同時,該設置會傳遞給瀏覽器,告訴瀏覽器輸出內容所采用的編碼。

          二,web開發中字符編碼幾處設置
                 對于web應用程序,和編碼有關的設置或者函數如下。
                  jsp編譯:指定文件的存儲編碼,很明顯,該設置應該置于文件的開頭。例如:<@pagepageEncoding="GBK"%>。另外,對于一般class文件,可以在編譯的時候指定編碼。
                  
          jsp輸出:指定文件輸出到browser是使用的編碼,該設置也應該置于文件的開頭。例如:<%@ page contentType="text/html; charset= GBK" %>。該設置和response.setCharacterEncoding("GBK")等效。
                  
          meta設置:指定網頁使用的編碼,該設置對靜態網頁尤其有作用。因為靜態網頁無法采用jsp的設置,而且也無法執行response.setCharacterEncoding()。例如:<META http-equiv="Content-Type" content="text/html; charset=GBK" />,如果同時采用了jsp輸出和meta設置兩種編碼指定方式,則jsp指定的優先。因為jsp指定的直接體現在response中。需要注意的是,apache有一個設置可以給無編碼指定的網頁指定編碼,該指定等同于jsp的編碼指定方式,所以會覆蓋靜態網頁中的meta指定。所以有人建議關閉該設置。
                  
          form設置:當瀏覽器提交表單的時候,可以指定相應的編碼。例如:<form accept-charset= "gb2312">。一般不必不使用該設置,瀏覽器會直接使用網頁的編碼。

          三,URL地址

                  URL地址中含有中文字符是很麻煩的,前面描述過使用GET方法提交表單的情況,使用GET方法時,參數就是包含在URL中。
                  URL編碼:對于URL中的一些特殊字符,瀏覽器會自動進行編碼。這些字符除了"/?&"等外,還包括unicode字符,比如漢子。這時的編碼比較特殊。
                  
          IE有一個選項"總是使用UTF-8發送URL",當該選項有效時,IE將會對特殊字符進行UTF-8編碼,同時進行URL編碼。如果改選項無效,則使用默認編碼"GBK",并且不進行URL編碼。但是,對于URL后面的參數,則總是不進行編碼,相當于UTF-8選項無效。比如"中文.html?a=中文",當UTF-8選項有效時,將發送鏈接"%e4%b8%ad%e6%96%87.html?a=\x4e\x2d\x65\x87";而UTF-8選項無效時,將發送鏈接"\x4e\x2d\x65\x87.html?a=\x4e\x2d\x65\x87"。注意后者前面的"中文"兩個字只有4個字節,而前者卻有18個字節,這主要時URL編碼的原因。當web server(tomcat)接收到該鏈接時,將會進行URL解碼,即去掉"%",同時按照ISO8859-1編碼(上面已經描述,可以使用URLEncoding來設置成其它編碼)識別。上述例子的結果分別是"\ue4\ub8\uad\ue6\u96\u87.html?a=\u4e\u2d\u65\u87"和"\u4e\u2d\u65\u87.html?a=\u4e\u2d\u65\u87",注意前者前面的"中文"兩個字恢復成了6個字符。這里用"\u",表示是unicode。所以,由于客戶端設置的不同,相同的鏈接,在服務器上得到了不同結果。這個問題不少人都遇到,卻沒有很好的解決辦法。所以有的網站會建議用戶嘗試關閉UTF-8選項。不過,下面會描述一個更好的處理辦法。
                  
          rewrite:熟悉的人都知道,apache有一個功能強大的rewrite模塊,這里不描述其功能。需要說明的是該模塊會自動將URL解碼(去除%),即完成上述web server(tomcat)的部分功能。有相關文檔介紹說可以使用[NE]參數來關閉該功能,但我試驗并未成功,可能是因為版本(我使用的是apache 2.0.54)問題。另外,當參數中含有"?& "等符號的時候,該功能將導致系統得不到正常結果。rewrite本身似乎完全是采用字節處理的方式,而不考慮字符串的編碼,所以不會帶來編碼問題。
                  
          URLEncode.encode():這是Java本身提供對的URL編碼函數,完成的工作和上述UTF-8選項有效時瀏覽器所做的工作相似。值得說明的是,java已經不贊成不指定編碼來使用該方法(deprecated)。應該在使用的時候增加編碼指定。當不指定編碼的時候,該方法使用系統默認編碼,這會導致軟件運行結果得不確定。比如對于"中文",當系統默認編碼為"gb2312"時,結果是"%4e%2d%65%87",而默認編碼為"UTF-8",結果卻是"%e4%b8%ad%e6%96%87",后續程序將難以處理。另外,這兒說的系統默認編碼是由運行tomcat時的環境變量LC_ALL和LANG等決定的,曾經出現過tomcat重啟后就出現亂碼的問題,最后才郁悶的發現是因為修改修改了這兩個環境變量。建議統一指定為"UTF-8"編碼,可能需要修改相應的程序。
                  
                  
          一個解決方案
                 上面說起過,因為瀏覽器設置的不同,對于同一個鏈接,web server收到的是不同內容,而軟件系統有無法知道這中間的區別,所以這一協議目前還存在缺陷。
                  
          針對具體問題,不應該僥幸認為所有客戶的IE設置都是UTF-8有效的,也不應該粗暴的建議用戶修改IE設置,要知道,用戶不可能去記住每一個web server的設置。所以,接下來的解決辦法就只能是讓自己的程序多一點智能:根據內容來分析編碼是否UTF-8。   
                  
          比較幸運的是UTF-8編碼相當有規律,所以可以通過分析傳輸過來的鏈接內容,來判斷是否是正確的UTF-8字符,如果是,則以UTF-8處理之,如果不是,則使用客戶默認編碼(比如"GBK"),下面是一個判斷是否UTF-8的例子,如果你了解相應規律,就容易理解

          public static boolean isValidUtf8(byte[] b,int aMaxCount){

                 
          int lLen=b.length,lCharCount=0;

                 
          for(int i=0;i<lLen && lCharCount<aMaxCount;++lCharCount){

                        
          byte lByte=b[i++];//to fast operation, ++ now, ready for the following for(;;)

                        
          if(lByte>=0continue;//>=0 is normal ascii

                        
          if(lByte<(byte)0xc0 || lByte>(byte)0xfreturn false;

                        
          int lCount=lByte>(byte)0xfc?5:lByte>(byte)0xf8?4

                               :lByte
          >(byte)0xf0?3:lByte>(byte)0xe0?2:1;

                        
          if(i+lCount>lLen) return false;

                        
          for(int j=0;j<lCount;++j,++i) if(b[i]>=(byte)0xc0return false;

                 }

                 
          return true;

          }

          相應地,一個使用上述方法的例子如下:

          public static String getUrlParam(String aStr,String aDefaultCharset)

          throws UnsupportedEncodingException{

                 
          if(aStr==nullreturn null;

                 
          byte[] lBytes=aStr.getBytes("ISO-8859-1");

                 
          return new String(lBytes,StringUtil.isValidUtf8(lBytes)?"utf8":aDefaultCharset);

          }

          不過,該方法也存在缺陷,如下兩方面:
                  
          沒有包括對用戶默認編碼的識別,這可以根據請求信息的語言來判斷,但不一定正確,因為我們有時候也會輸入一些韓文,或者其他文字。
                  
          可能會錯誤判斷UTF-8字符,一個例子是"學習"兩個字,其GBK編碼是" \xd1\xa7\xcf\xb0",如果使用上述isValidUtf8方法判斷,將返回true。可以考慮使用更嚴格的判斷方法,不過估計效果不大。
                  
          有一個例子可以證明google也遇到了上述問題,而且也采用了和上述相似的處理方法,比如,如果在地址欄中輸入"http://www.google.com/search?hl=zh-CN&newwindow=1&q=學習",google將無法正確識別,而其他漢字一般能夠正常識別。最后,應該補充說明一下,如果不使用rewrite規則,或者通過表單提交數據,其實并不一定會遇到上述問題,因為這時可以在提交數據時指定希望的編碼。另外,中文文件名確實會帶來問題,應該謹慎使用。

          四,過濾器
                  
          如果需要統一設置編碼,則通過filter進行設置是個不錯的選擇。在filter class中,可以統一為需要的請求或者回應設置編碼。參加上述setCharacterEncoding()。這個類apache已經給出了可以直接使用的例SetCharacterEncodingFilter。

          posted on 2008-01-05 17:36 zhhang920 閱讀(310) 評論(0)  編輯  收藏 所屬分類: J2EE

          主站蜘蛛池模板: 蓬安县| 根河市| 秦安县| 南江县| 曲周县| 沙坪坝区| 潞城市| 福海县| 泗水县| 德阳市| 佛教| 孟村| 兴安盟| 金湖县| 宁德市| 合肥市| 株洲市| 陆丰市| 邢台市| 大厂| 贞丰县| 伊春市| 游戏| 深水埗区| 定西市| 汤阴县| 大石桥市| 库伦旗| 盐津县| 台州市| 海安县| 石棉县| 洪泽县| 龙川县| 咸阳市| 武义县| 柘荣县| 霞浦县| 郓城县| 贵南县| 中卫市|