• <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>

            關于Bash shell在不同locale下的”異常”表現之探討

                                                  peakflys原創作品,轉載請注明原作者和源鏈接
               現有一個文本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="GM"/>
                    <Word content="sex"/>
                    <Word content="G M"/>
                    <Word content="fuck"/>
                    <Word content="shit"/>

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

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

                     sort –u test.xml

            但是結果卻大跌眼鏡,輸出如下:

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

            因為例子test.xml中內容只有十幾行,很明顯可以看出來去重后的結果不對!

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

            輸出結果同上……

               那使用文本處理神器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"/>

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

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

            估計大家通過文章的標題早就知道原因了,廢話不多說,直接看一下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設置:

            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=

            依照文檔中說明重新設置一下本地locale的環境變量:LANG=C

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

            Linux下有不少命令的輸出是和本地的locale設置有關的,除sort和uniq外,還有ls等,在此不再擴展展開。

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

            其實sort的實現代碼很精致也很高效,有興趣的同學可以自行察看,下面僅摘錄一些和本篇文章相關的代碼。

            首先是一些初始化代碼:

                 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上面已經注釋了,第二個參數為空字符時取本機的locale設置,hard_locale的實現代碼就不貼了,它主要是通過查找locale中LC_COLLATE有沒有"C"或者"POSIX"。

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

            下面給出sort實現的核心比較操作的代碼:

                  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注:上文提到的這個標記位! 
                    {
                      /* 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注:設置LANG=C后,程序邏輯在這里?
                      diff = alen < blen ? -1 : alen != blen;
                  
                    return reverse ? -diff : diff;
               }

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

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

            那我們繼續看xmemcoll0相關的實現:

                 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關于locale的比較實現最終是通過庫函數strcoll來實現的(礙于篇幅,這個glibc庫函數的實現代碼我就不貼了,感興趣的可以自行下載研究),這個函數通過調用__strcoll_l來根據不同locale定義的weights等信息來比較兩個字符串。
                  其他的命令,諸如uniq、ls等的情況和sort命令大致一樣,大家可以自己down下GNU的coreutils來研究。

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

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

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

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

            <2012年7月>
            24252627282930
            1234567
            891011121314
            15161718192021
            22232425262728
            2930311234

            導航

            統計

            公告

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

            常用鏈接

            留言簿(4)

            隨筆分類

            隨筆檔案

            文章檔案

            搜索

            最新評論

            閱讀排行榜

            評論排行榜

            国产欧美久久久精品| 久久人人爽人人爽人人av东京热 | 国内精品久久久久久久97牛牛| 久久99热这里只有精品国产| 久久精品无码专区免费青青| 国产精自产拍久久久久久蜜| 国内精品伊人久久久影院| 精品久久久久久久久中文字幕| 久久九色综合九色99伊人| 性做久久久久久久| 国内精品伊人久久久久影院对白| 77777亚洲午夜久久多人| 国产精品99久久久久久人| 久久综合九色综合久99| 一级a性色生活片久久无| 热久久这里只有精品| 精品久久久久久国产| 久久久久噜噜噜亚洲熟女综合| 看久久久久久a级毛片| 亚洲va久久久久| 久久99精品久久久久久齐齐| 国产精品禁18久久久夂久| 久久精品国产乱子伦| 久久精品免费大片国产大片| 久久成人精品视频| 久久精品水蜜桃av综合天堂 | 久久精品中文字幕一区| 丰满少妇人妻久久久久久| 久久久久久免费视频| 欧美国产精品久久高清| 久久91精品综合国产首页| 青青热久久综合网伊人| 久久综合九色综合97_久久久| 久久99精品久久只有精品 | 国产成人久久777777| 996久久国产精品线观看| 97r久久精品国产99国产精| 1000部精品久久久久久久久| 久久精品国产亚洲av高清漫画 | 色综合合久久天天给综看| 精品国产乱码久久久久久浪潮|