• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            關(guān)于Bash shell在不同locale下的”異常”表現(xiàn)之探討

                                                  peakflys原創(chuàng)作品,轉(zhuǎn)載請注明原作者和源鏈接
               現(xiàn)有一個文本test.xml,內(nèi)容大致如下:
                     <Word content="㈠"/>
                    <Word content="㈡"/>
                    <Word content="㈢"/>
                    <Word content="㈣"/>
                    <Word content="㈤"/>
                    <Word content="㈥"/>
                    <Word content="㈦"/>
                    <Word content="㈧"/>
                    <Word content="㈨"/>
                    <Word content="㈩"/>
                    <Word content="㊣"/>
                    <Word content="GM"/>
                    <Word content="GM"/>
                    <Word content="sex"/>
                    <Word content="G M"/>
                    <Word content="fuck"/>
                    <Word content="shit"/>

            文本中顯而易見含有重復(fù)行,假如讓你來去重的話,你可能會說so easy,直接手動去掉多出來的那項(xiàng)GM就行了。但是如果這個文本有上萬行,而且上下文內(nèi)容沒有這么明顯的相似性歸類的話,又要怎么辦?

            在windows下相信大家很容易寫出一個小程序來處理這樣的文本,而在linux下工作的人肯定馬上會想到諸如sort、uniq、awk等命令,這種問題一條指令就搞定了嘛:

                     sort –u test.xml

            但是結(jié)果卻大跌眼鏡,輸出如下:

                    <Word content="㈡"/>
                    <Word content="㈠"/>
                    <Word content="GM"/>
                    <Word content="sex"/>
                    <Word content="fuck"/>

            因?yàn)槔觮est.xml中內(nèi)容只有十幾行,很明顯可以看出來去重后的結(jié)果不對!

                  那么使用uniq 呢?
                     sort test.xml  | uniq

            輸出結(jié)果同上……

               那使用文本處理神器awk呢?
                     awk '!a[$0]++' test.xml

            輸出如下:
                     <Word content="㈠"/>

                    <Word content="㈡"/>
                    <Word content="㈢"/>
                    <Word content="㈣"/>
                    <Word content="㈤"/>
                    <Word content="㈥"/>
                    <Word content="㈦"/>
                    <Word content="㈧"/>
                    <Word content="㈨"/>
                    <Word content="㈩"/>
                    <Word content="㊣"/>
                    <Word content="GM"/>
                    <Word content="sex"/>
                    <Word content="G M"/>
                    <Word content="fuck"/>
                    <Word content="shit"/>

            神器就是神器!這個結(jié)果才是我們需要的嘛。

            但是sort和uniq為什么篩選出的結(jié)果有問題呢?

            估計(jì)大家通過文章的標(biāo)題早就知道原因了,廢話不多說,直接看一下sort 的manual吧。里面有這么一句:

                 *** WARNING *** The locale specified by the environment affects sort order.  Set LC_ALL=C to get the traditional sort order that uses native byte values.

            先看一下我本地的LANG設(shè)置:

            LANG=en_US.UTF-8

            LC_CTYPE="en_US.UTF-8"

            LC_NUMERIC="en_US.UTF-8"

            LC_TIME="en_US.UTF-8"

            LC_COLLATE="en_US.UTF-8"

            LC_MONETARY="en_US.UTF-8"

            LC_MESSAGES="en_US.UTF-8"

            LC_PAPER="en_US.UTF-8"

            LC_NAME="en_US.UTF-8"

            LC_ADDRESS="en_US.UTF-8"

            LC_TELEPHONE="en_US.UTF-8"

            LC_MEASUREMENT="en_US.UTF-8"

            LC_IDENTIFICATION="en_US.UTF-8"

            LC_ALL=

            依照文檔中說明重新設(shè)置一下本地locale的環(huán)境變量:LANG=C

            之后使用sort或者uniq命令輸出正確。

            Linux下有不少命令的輸出是和本地的locale設(shè)置有關(guān)的,除sort和uniq外,還有l(wèi)s等,在此不再擴(kuò)展展開。

            還如往常一樣,上面講述的僅僅是一個結(jié)果,以及淺層次的原因,為了對得起題目中的”探討”兩個字,咱們繼續(xù)細(xì)究一下sort的實(shí)現(xiàn),以及發(fā)生這種情況的具體原因。

            其實(shí)sort的實(shí)現(xiàn)代碼很精致也很高效,有興趣的同學(xué)可以自行察看,下面僅摘錄一些和本篇文章相關(guān)的代碼。

            首先是一些初始化代碼:

                 initialize_main (&argc, &argv);
                 set_program_name (argv[0]);
                 setlocale (LC_ALL, ""); //peakflys引manual注: If locale  is "", each part of the locale that should be modified is set according to the environment variables.
                 bindtextdomain (PACKAGE, LOCALEDIR);
                 textdomain (PACKAGE);
                 initialize_exit_failure (SORT_FAILURE);
                 hard_LC_COLLATE = hard_locale (LC_COLLATE);

            setlocale上面已經(jīng)注釋了,第二個參數(shù)為空字符時取本機(jī)的locale設(shè)置,hard_locale的實(shí)現(xiàn)代碼就不貼了,它主要是通過查找locale中LC_COLLATE有沒有"C"或者"POSIX"。

            有的話置標(biāo)記hard_LC_COLLATE為false,否則為true。這個標(biāo)志位很重要,具體使用見下文。

            下面給出sort實(shí)現(xiàn)的核心比較操作的代碼:

                  static int
                 compare (struct line const *a, struct line const *b)
                 {
                    int diff;
                    size_t alen, blen;

                    /* First try to compare on the specified keys (if any).
                       The only two cases with no key at all are unadorned sort,
                       and unadorned sort -r. */
                    if (keylist)
                    {
                        diff = keycompare (a, b);
                        if (diff || unique || stable)
                          return diff;
                    }
                  
                    /* If the keys all compare equal (or no keys were specified)
                       fall through to the default comparison.  */
                    alen = a->length - 1, blen = b->length - 1;
                      
                    if (alen == 0)
                      diff = - NONZERO (blen);
                    else if (blen == 0)
                      diff = 1;
                    else if (hard_LC_COLLATE)     //peakflys注:上文提到的這個標(biāo)記位! 
                    {
                      /* Note xmemcoll0 is a performance enhancement as
                        it will not unconditionally write '\0' after the
                        passed in buffers, which was seen to give around
                        a 3% increase in performance for short lines.  */
                        diff = xmemcoll0 (a->text, alen + 1, b->text, blen + 1);
                    }
                    else if (! (diff = memcmp (a->text, b->text, MIN (alen, blen))))  //peakflys注:設(shè)置LANG=C后,程序邏輯在這里?
                      diff = alen < blen ? -1 : alen != blen;
                  
                    return reverse ? -diff : diff;
               }

            上面代碼注釋很清楚相信大家已經(jīng)知道為什么設(shè)置LANG=C (linux系統(tǒng)中"C"和"POSIX"在locale意義上等價)后,sort操作就正常了。

            那么為什么使用UTF-8的locale時會出問題呢?

            那我們繼續(xù)看xmemcoll0相關(guān)的實(shí)現(xiàn):

                 int
                 xmemcoll0 (char const *s1, size_t s1size, char const *s2, size_t s2size)
                 {
                    int diff = memcoll0 (s1, s1size, s2, s2size);
                    int collation_errno = errno;
                    if (collation_errno)
                      collate_error (collation_errno, s1, s1size - 1, s2, s2size - 1); 
                    return diff;
                  }
                  int
                  memcoll0 (char const *s1, size_t s1size, char const *s2, size_t s2size)
                  {
                     if (s1size == s2size && memcmp (s1, s2, s1size) == 0)
                     {   
                        errno = 0;
                        return 0;
                     }   
                    else
                      return strcoll_loop (s1, s1size, s2, s2size);
                  }
                  static int 
                  strcoll_loop (char const *s1, size_t s1size, char const *s2, size_t s2size)
                  {     
                    int diff;
                
                    while (! (errno = 0, (diff = strcoll (s1, s2)) || errno))
                    {
                        /* strcoll found no difference, but perhaps it was fooled by NUL
                           characters in the data.  Work around this problem by advancing
                           past the NUL chars.  */
                        size_t size1 = strlen (s1) + 1;
                        size_t size2 = strlen (s2) + 1;
                        s1 += size1;
                        s2 += size2;
                        s1size -= size1;
                        s2size -= size2;

                        if (s1size == 0) 
                          return - (s2size != 0);
                        if (s2size == 0)
                          return 1;
                      }   
              
                    return diff;
                  }

            至此我們可以看到sort關(guān)于locale的比較實(shí)現(xiàn)最終是通過庫函數(shù)strcoll來實(shí)現(xiàn)的(礙于篇幅,這個glibc庫函數(shù)的實(shí)現(xiàn)代碼我就不貼了,感興趣的可以自行下載研究),這個函數(shù)通過調(diào)用__strcoll_l來根據(jù)不同locale定義的weights等信息來比較兩個字符串。
                  其他的命令,諸如uniq、ls等的情況和sort命令大致一樣,大家可以自己down下GNU的coreutils來研究。

            眼尖的同學(xué)可能會發(fā)現(xiàn)上面例子中文本就是大天朝下游戲臟詞庫中常見的一部分,而這些內(nèi)容時常需要根據(jù)當(dāng)前情況追加一些新的紀(jì)錄,所以如果沒有大天朝的奇葩制度和游戲玩家的“無窮智慧”也就沒有文中開場例子的使用場景。

            講了那么多遍locale,可能有些人要問,什么是locale?
                  其實(shí)locale就是根據(jù)計(jì)算機(jī)用戶所使用的語言,所在國家或者地區(qū),以及當(dāng)?shù)氐奈幕瘋鹘y(tǒng)所定義的一個軟件運(yùn)行時的語言環(huán)境。狹隘的說,它規(guī)定了字符集,以及這種字符集具體的展示方式。 bash shell之所以很多命令同locale相關(guān),也是基于不同地區(qū)、不同語言的用戶可以通過選擇各自的locale來達(dá)到定制化的效果。
                  更具體的介紹就不在這里討論了,感興趣的朋友可以通過下面鏈接了解:

            http://m.shnenglu.com/peakflys/articles/209773.html
                                                                                            by peakflys 08:51:19 Tuesday, February 10, 2015

            posted on 2015-02-10 09:10 peakflys 閱讀(1807) 評論(0)  編輯 收藏 引用 所屬分類: 操作系統(tǒng)雜談

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

            導(dǎo)航

            統(tǒng)計(jì)

            公告

            人不淡定的時候,就愛表現(xiàn)出來,敲代碼如此,偶爾的靈感亦如此……

            常用鏈接

            留言簿(4)

            隨筆分類

            隨筆檔案

            文章檔案

            搜索

            最新評論

            閱讀排行榜

            評論排行榜

            亚洲精品乱码久久久久久| 狠狠色综合网站久久久久久久| 久久只有这精品99| 人妻精品久久无码区| 日本道色综合久久影院| 久久人人爽人人澡人人高潮AV | 久久久久久人妻无码| 国内精品久久久久影院免费| 久久精品成人免费观看97| 亚洲日本久久久午夜精品| 2021少妇久久久久久久久久| 人妻丰满?V无码久久不卡| 久久久久久九九99精品| 欧美亚洲国产精品久久久久| 久久精品一区二区| 欧美精品乱码99久久蜜桃| 一本一道久久精品综合| 亚洲精品午夜国产VA久久成人| 国产精品99久久久久久猫咪| 久久无码人妻一区二区三区| 欧美伊人久久大香线蕉综合69| 成人久久综合网| 色婷婷综合久久久久中文| 久久这里的只有是精品23| 久久久久99精品成人片| 久久国产一区二区| 久久99国产综合精品| 无码日韩人妻精品久久蜜桃 | 久久亚洲2019中文字幕| 狠狠色丁香久久综合婷婷| 无码AV中文字幕久久专区| 久久精品卫校国产小美女| 香港aa三级久久三级老师2021国产三级精品三级在 | 久久偷看各类wc女厕嘘嘘| 伊人久久综合精品无码AV专区| 久久久久国产亚洲AV麻豆| 久久99精品久久久久久9蜜桃| 中文字幕久久欲求不满| 色综合久久88色综合天天| 精品国产乱码久久久久久浪潮| 国内精品久久久久久麻豆 |