<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    成就夢(mèng)想

      BlogJava :: 首頁(yè) :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理 ::
      21 隨筆 :: 22 文章 :: 6 評(píng)論 :: 0 Trackbacks
        我們部門底層的web應(yīng)用有一套處理編碼的流程機(jī)制,主要處理因歷史原因或者跨部門產(chǎn)品之間gbk和utf8剪不清理還亂的關(guān)系。
    前2天同事有詢問(wèn)相關(guān)編碼的問(wèn)題,在此做個(gè)整理,希望能夠?qū)Υ蠹矣兴鶐椭?br />
    首先是編碼的歷史,這是一個(gè)很有意思的解讀 。寫的很幽默,便于理解。

    下面主要寫于與java想關(guān)的編碼,主要解讀unicode ,utf8 和gbk。

    JVM里面的任何字符串資源都是Unicode,就是說(shuō),任何String類型的數(shù)據(jù)都是Unicode編碼。沒(méi)有例外,因此我們可以這么說(shuō),JVM里面的String是不帶編碼的。因?yàn)樗陀星抑粚?duì)應(yīng)一種Unicode。
    一個(gè)字符的Unicode編碼是確定的。也就是說(shuō)Unicode是一種字符集,里面字符與編碼是一一對(duì)應(yīng)的,這里有個(gè)碼表可查,unicode 碼表。但是在實(shí)際傳輸過(guò)程中,由于不同系統(tǒng)平臺(tái)的設(shè)計(jì)不一定一致,以及出于節(jié)省空間的目的,對(duì)Unicode編碼的實(shí)現(xiàn)方式有所不同。Unicode的實(shí)現(xiàn)方式稱為Unicode轉(zhuǎn)換格式(Unicode Transformation Format,簡(jiǎn)稱為UTF)。我們常用的就是UTF8.
    UTF8是如何存儲(chǔ)一個(gè)Unicode編碼的呢。也就是utf8作為一種Unicode Transformation Format是如何工作的呢?
    首先utf8 是可變長(zhǎng)的,UTF-8使用一至四個(gè)字節(jié)為每個(gè)字符編碼。參照下表,我們把精力放在第1列,第3列,和注釋。
    對(duì)于ASCII字符,可以用七個(gè)bit位來(lái)表示,x6 x5 x4 x3 x2 x1 x0.第八個(gè)bit永遠(yuǎn)是0。
    第128到2047個(gè)字節(jié),要用10個(gè)bit來(lái)表示,110yyyyy(C0-DF) 10zzzzzz(80-BF)
    第2048到65535個(gè)字節(jié),要用16個(gè)bit來(lái)表示,Utf-8把這些字節(jié)編成下面這樣的三個(gè)byte。1110xxxx(E0-EF) 10yyyyyy 10zzzzzz
    大于65535其余用4個(gè)byte來(lái)表示。

    舉個(gè)例子:“中國(guó)”的中,unicode編碼是“\u4e2d", 對(duì)應(yīng)的編碼除了查表,java可以用命令行,運(yùn)行 native2ascii 進(jìn)行轉(zhuǎn)化。
    用window 自帶的附件中的計(jì)算器(查看->科學(xué)型),轉(zhuǎn)化成10進(jìn)制為20013,二進(jìn)制是100111000101101 
    通過(guò)上面的表,可知,轉(zhuǎn)化成utf8后為三個(gè)字節(jié)。
    只需要將剛才轉(zhuǎn)化的二進(jìn)制(上面標(biāo)紅的)將下面的xxxx,yyyyyy,zzzzzz補(bǔ)齊即可。
    1110xxxx(E0-EF) 10yyyyyy 10zzzzzz,我們從低位開始補(bǔ)起,不夠的用0補(bǔ)齊。
    11100100 10111000 10101101  ,換成16進(jìn)制為E4 B8 AD。
    好了我們用java代碼來(lái)驗(yàn)證下,是否正確。
    public static void main(String[] args) {
            String ha 
    = "";
            
    byte b[] = null;
            
    try {
                b 
    = ha.getBytes("utf-8");
            } 
    catch (Exception e) {
                System.exit(
    -1);
            }

            
    for (int i = 0; i < b.length; i++) {
                System.out.print(Integer.toHexString(b[i]).substring(
    6+ " ");
            }

        }
    輸出果然是:e4  b8 ad。
    utf8 wiki中有下描述:
    • 對(duì)于UTF-8編碼中的任意字節(jié)B,如果B的第一位為0,則B為ASCII碼,并且B獨(dú)立的表示一個(gè)字符;
    • 如果B的第一位為1,第二位為0,則B為一個(gè)非ASCII字符(該字符由多個(gè)字節(jié)表示)中的一個(gè)字節(jié),并且不為字符的第一個(gè)字節(jié)編碼;
    • 如果B的前兩位為1,第三位為0,則B為一個(gè)非ASCII字符(該字符由多個(gè)字節(jié)表示)中的第一個(gè)字節(jié),并且該字符由兩個(gè)字節(jié)表示;
    • 如果B的前三位為1,第四位為0,則B為一個(gè)非ASCII字符(該字符由多個(gè)字節(jié)表示)中的第一個(gè)字節(jié),并且該字符由三個(gè)字節(jié)表示;
    • 如果B的前四位為1,第五位為0,則B為一個(gè)非ASCII字符(該字符由多個(gè)字節(jié)表示)中的第一個(gè)字節(jié),并且該字符由四個(gè)字節(jié)表示;

    因此,對(duì)UTF-8編碼中的任意字節(jié),根據(jù)第一位,可判斷是否為ASCII字符;根據(jù)前二位,可判斷該字節(jié)是否為一個(gè)字符編碼的第一個(gè)字節(jié); 根據(jù)前四位(如果前兩位均為1),可確定該字節(jié)為字符編碼的第一個(gè)字節(jié),并且可判斷對(duì)應(yīng)的字符由幾個(gè)字節(jié)表示;根據(jù)前五位(如果前四位為1),可判斷編碼 是否有錯(cuò)誤或數(shù)據(jù)傳輸過(guò)程中是否有錯(cuò)誤。


    反過(guò)來(lái),我們還是拿剛才的”中“為例,11100100 10111000 10101101 ,第一個(gè)字節(jié)開始為110,則讀第二個(gè)字節(jié)為10,第三個(gè)字節(jié)為10,則認(rèn)為是utf8字符。
    于是就有了一個(gè)那個(gè)經(jīng)典的“聯(lián)通"干不過(guò)”移動(dòng)“的經(jīng)典段子。
    我們?cè)趚p下,隨便建立一個(gè)文件,輸入"聯(lián)通",保存,這時(shí)你在打開是,發(fā)現(xiàn)”聯(lián)通"2個(gè)字符不見了。奇怪嗎??????
    我們知道默認(rèn)保存的編碼是ANSI,實(shí)際也是類GBK的編碼。
    對(duì)應(yīng)16進(jìn)制為c1 aa cd a8, 轉(zhuǎn)化成二進(jìn)制為11000001 10101010 11001101 10101000 ,我們來(lái)看,110xxxxx,10xxxxxx 正好符合utf8的形式。
    這時(shí)候文件編寫器以為你的文件是utf8的文件,然后默認(rèn)已utf8的形式給你打開展示。于是就出現(xiàn)亂碼了。如果你在”聯(lián)通“后面隨便加幾個(gè)字符。就不出出現(xiàn)靈異事件了。

    那么我們繼續(xù)討論 GBK和Unicode是什么關(guān)系呢?
    實(shí)際上GBK我們可以看做是字符集,他也有自己一一對(duì)應(yīng)的碼表。google一下,很容易查到。這里有個(gè)Unicode和GBk對(duì)應(yīng)的表Unicode-GBk
    在java中,
    "我愛(ài)你莎莎".getBytes("gbk");
    進(jìn)行轉(zhuǎn)化,其實(shí)就是類似查一個(gè)Unicode和GBk對(duì)應(yīng)表進(jìn)行轉(zhuǎn)化的。大家看一下Charset這個(gè)抽象類的那些子類就明白了。
    通過(guò)上面的描述GBk和UTF8關(guān)系也就很明朗了,完全可以通過(guò)Unicode進(jìn)行中轉(zhuǎn)。

    同事在詢問(wèn)編碼的問(wèn)題時(shí),一開始對(duì)類似如下代碼,相互轉(zhuǎn)變不太理解。
    byte b1[] = null;
            b1 = "我愛(ài)你莎莎".getBytes("gbk");
            System.out.println(new String(b1,"gbk"));
            byte b2[] = null;
            b2 = "我愛(ài)你莎莎".getBytes("utf8");
            System.out.println(new String(b2,"utf8"));
            System.out.println(new String (new String (b2,"gbk").getBytes("gbk"),"utf8"));
    其實(shí)我們可以把getBytes("gbk"),這個(gè)函數(shù)當(dāng)做將unicode用gkb加密的過(guò)程,而new String(”xxx“,"編碼”)看成是解密的一個(gè)過(guò)程。

    大家思考一下最后面的那個(gè)輸出可以得到正確的結(jié)果嗎?為什么?

    下面我們來(lái)討論 ,通過(guò)http協(xié)議下的url傳輸后,編碼轉(zhuǎn)化問(wèn)題。
    首先說(shuō)明的是本人本地默認(rèn)編碼是gbk。
    我們只用Servlet,不使用任何框架比如spring(因?yàn)槭褂每蚣軙r(shí),框架也有一套自己自己的機(jī)制)如下代碼
    public class HttpEncode extends HttpServlet {
        @Override
        
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            String str 
    = req.getQueryString();
            System.out.println(req.getCharacterEncoding());

            String encode 
    = null;
            
    try {
                encode 
    = req.getParameter("encode");
            } 
    catch (Exception e) {
                e.printStackTrace();
            }

            System.out.println(str);
            System.out.println(encode);

        }

    }
    我們分別用jetty(版本6.1)和resin(版本3.1.8)下容器,測(cè)試如下請(qǐng)求     127.0.0.1/test?encode=%B9%FE  其中%B9%FE為GBk的編碼的漢字”哈“
    jetty容器下輸出為
    null
    encode=%B9%FE
    ?
    resin下為:
    null
    encode=%B9%FE
    null

    換做127.0.0.1/test?encode=%E5%93%88   ,utf8編碼的”哈“
    jetty和resin下都輸出如下
    null
    encode=%E5%93%88

    為什么會(huì)是這樣?
    我們拿jetty分析,在jetty的源碼中,
       public String getParameter(String name)
        {
            
    if (!_paramsExtracted) 
                extractParameters();
            
    return (String) _parameters.getValue(name, 0);
        }

    對(duì)應(yīng)的
    extractParameters(); 部分代碼
     if (_queryEncoding==null)
                    _uri.decodeQueryTo(_baseParameters);
             
    然后
       public void decodeQueryTo(MultiMap parameters)
        {
            if (_query==_fragment)
                return;
            _utf8b.reset();
            UrlEncoded.decodeUtf8To(_raw,_query+1,_fragment-_query-1,parameters,_utf8b);
        }






    也就是如果_queryEncoding為null時(shí),默認(rèn)是用utf8進(jìn)行解碼的。而resin也不例外。
    jetty中_queryEncoding的值可以通過(guò)org.mortbay.jetty.Request.queryEncoding 這個(gè)屬性給賦值而resin采用的是req.getCharacterEncoding()中的值為標(biāo)準(zhǔn)。
    要想在jetty下 127.0.0.1/test?encode=%B9%FE,獲取到正確的字符,代碼如下
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            String str 
    = req.getQueryString();
            System.out.println(req.getCharacterEncoding());
            req.setAttribute(
    "org.mortbay.jetty.Request.queryEncoding""gbk");
            String encode 
    = null;
            
    try {
                encode 
    = req.getParameter("encode");
            } 
    catch (Exception e) {
                e.printStackTrace();
            }

            System.out.println(str);
            System.out.println(encode);

        }

    resin下只需要
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            String str 
    = req.getQueryString();
            req.setCharacterEncoding(
    "gbk");
            System.out.println(req.getCharacterEncoding());
            String encode 
    = null;
            
    try {
                encode 
    = req.getParameter("encode");
            } 
    catch (Exception e) {
                e.printStackTrace();
            }

            System.out.println(str);
            System.out.println(encode);

        }

    通過(guò)上面想說(shuō)明的是,不同的容器,默認(rèn)編碼的策略是不一致的。只要我們了解編碼的基礎(chǔ)知識(shí)。通過(guò)一些封裝就很容易掌控這個(gè)局面。





    參考資料:
    Unicode wiki:   http://zh.wikipedia.org/wiki/Unicode
    jetty 源碼
    posted on 2012-04-18 11:38 iamct 閱讀(1705) 評(píng)論(0)  編輯  收藏 所屬分類: 基礎(chǔ)知識(shí)

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


    網(wǎng)站導(dǎo)航:
     
    主站蜘蛛池模板: jizz在线免费观看| 亚洲av无码一区二区三区不卡| 亚洲免费视频网站| 日韩大片在线永久免费观看网站| 亚洲国产精品综合久久2007| 亚洲日韩精品一区二区三区无码| 处破痛哭A√18成年片免费| 免费国产黄网站在线观看视频| 未满十八私人高清免费影院| 亚洲男人天堂2018av| 亚洲视频在线观看免费视频| 亚洲国产精品毛片av不卡在线| AV片在线观看免费| 免费黄色福利视频| 久久久精品免费视频| 中文字幕不卡高清免费| 亚洲AV日韩AV无码污污网站| 亚洲自国产拍揄拍| 亚洲色欲www综合网| 亚洲国产日韩一区高清在线| 亚洲熟妇av一区二区三区| 亚洲精品NV久久久久久久久久| 浮力影院第一页小视频国产在线观看免费 | 成人免费无码大片a毛片| 国产h视频在线观看网站免费| 日韩免费的视频在线观看香蕉 | 亚洲精品国产V片在线观看| 免费观看国产小粉嫩喷水| 国产精品酒店视频免费看| 国产美女精品视频免费观看| 午夜免费福利在线| 四虎成人免费观看在线网址| 毛片免费在线观看网址| 国内免费高清在线观看| 毛片高清视频在线看免费观看| 国产电影午夜成年免费视频 | 日本久久久久亚洲中字幕| 亚洲人成网址在线观看| 亚洲午夜久久久精品影院| 亚洲视频在线免费播放| 亚洲一级毛片免费看|