《專題報導》 Regular expression 與 GNU grep 的中文化 (作者 邱展毅) 一、前言 打開電視、收音機、報章雜誌,細心的你也許已發覺到資訊量正 快速的成長中。若你連上網際網路(Internet),相信News、BBS、 Gopher、WWW(Word Wide Web)的資訊量更會使你看的目不暇給。聰 明的你會如何運用這些資訊呢?或者已被這龐大的資訊巨獸逼迫的 不知所措? 對生活在資訊高度膨脹環境下的現代人而言,擁有好的檢索工具 ,以過濾出真正有用的資訊,的確是一件不可或缺的事!本文將介 紹在UNIX上常用的一個檢索工具---grep,並說明以GNU grep爲基礎 ,所完成可處理「中文regular expression」的工具---cgrep。 二、Regular expression Regular expression可分爲Standard regular expression 及 Extended regular expression,前者之組成元素爲:普通字元、^ 、、〔...〕、. 、*、-、〔^...〕、掤、痎、、m,n;後者除了 前者之組成元素外,增加了四個運算子:+、? 、(...)及|。 regular repression是一種很值得推崇的字串表達方式,可以簡化 許多複雜的字串處理工作,提升工作效率。然而本文的重點並不是 介紹它的使用方法,故僅列舉幾個實例於【表一】以供參考。有興 趣的讀者可參考本通訊第十一卷05、06兩期之「Regular Expression簡介」一文或UNIX指令Sed、egrep之man page。 表一 regular expression使用實例 ┌─────────────────────────────┐ │1、"[Tt]here"表示字串"There"或"there"。 │ │2、"tr.*al"表示以"tr"開始,"al"結束的所有字串。 │ │3、"[a-zA-Z]/{0,20/}"表示由0-20個英文字母(不分大小寫)所組│ │ 成的字串。 │ │4、"[Tt]here|tr.*al|[a-zA-Z]/{0,20/}表示滿足上述1、2或3 │ │ 之所有字串。 │ └─────────────────────────────┘ UNIX上有許多已被廣泛使用的工具,諸如grep、egrep、awk、 sed、vi、emacs...等,這些工具皆可使用regular expression來指 定欲找尋的字串,並可結合各工具的其他功能,對找到的資料做進 一步的處理,使得它們成爲功能強大且受歡迎的工具。 不過令人感到遺憾的是這些好用的工具所支援的regular expression,到目前爲止僅可處理ASCII字元(注一)所組成的 regular expression,如【表一】所列出的例子。對於 Big5中文碼 的處理,常有預期之外的結果產生,使得使用者無法完全信任這些 工具,而必須多做一些檢驗的動作,造成使用上的不便及時間的浪 費! 【表二】說明造成這種情況的原因。 表二 regular expression與Big5中文字 ┌─────────────────────────────────────┐ │1.Big5中文碼(2bytes code)之組成形式如下圖所示: │ │ ┌───────┬───────┐ │ │ │ High Byte │ Low Byte │ │ │ ├───────┼───────┤ │ │ │ 0xA1-0xFE │ 0x40-0x7E │ │ │ │ │ or │ │ │ │ │ 0xA1-0xFE │ │ │ └───────┴───────┘ │ │2.由上圖可看出Big5中文字之high byte及low byte有重複部分(0xA1-0xFE)。若 │ │ 中文字之high byte及low byte皆落在0xA1-0xFE 這個範圍內,則可能產生第一個│ │ 中文字的low byte與第二個中文字的high byte所組成的字仍是一個合法中文字的│ │ 情形,這就是造成混淆的原因之一。例如:資、料、網、不、中、之、互、仍、今│ │ 、內、分、及、天、太、少、手、全、文、方、日、月、它、必、用、民...,這│ │ 些常用到的字都是會產生混淆的字。 │ │ │ │3.會產生混淆的字共有8050個(Big5 A4A1-F9DC),若包括全形符號及造字區,則不只│ │ 這些。 │ │ │ │4.Regular expression所構成的樣式(pattern)若含有運算子'. '、'*'、/{m,n/}、│ │ 〔...〕,當與中文字做比對時,可能會將中文字截一半。例如:pattern="資." │ │ 並不會找到"資訊",只找到"資"及"訊"的high byte。 │ │ │ │5.Regular expression之character class運算子〔...〕內部只能辨識1byte字元(a │ │ 、z、3...),無法辨識2bytes字元(Big5中文碼)。例如:pattern="資〔訊料〕"無│ │ 法正確比對"資訊"或"資料"等字串。 │ └─────────────────────────────────────┘ 假若我們能解決【表二】所列出的問題,對Big5 中文環境的使 用者而言,相信會是一個很好的消息!基於這個理念,我們首先完 成了GNU grep的中文化,解決了上述問題,爲各位呈獻一個支援全 中文regular expression的好工具。 三、GNU grep的中文化 傳統UNIX指令中, grep家族---grep、egrep、fgrep(注二)是一 組非常好用而且使用頻率相當高的檢索工具。藉由它們,可以從一 羣資料檔中過濾出我們想要的資料,進而提供我們有用的資訊。不 過若將其應用於檢索中文資料,可能常有受騙的感覺!底下說明中 文化的動機與過程: 1.中文化動機---從資料檔(test.doc)中檢索資料 ┌─────────────────────────┐ │ 蒐集相關資料。 │ │ 以正確的資訊做判斷。 │ │ 全文檢索滿足您的需要。 │ │ 之乎者也成功的中文化。 │ │ 在瀏灠畫面中即無法顯示出來。 │ │ 擁有全文檢索系統使您的工作更加有效率。 │ │ This is the hour Madam Entreated me to call her. │ └─────────────────────────┘ 例一、執行egrep "的|之" test.doc,結果如下: ┌───────────────────┐ │以正確的資訊做判斷。 │ │ -- │ │全文檢索滿足您的需要。 │ │ -- │ │之乎者也成功的中文化。 │ │在瀏灠畫面中即無法顯示出來。 │ │ ---- │ │擁有全文檢索系統使您的工作更加有效率。 │ │ -- │ └───────────────────┘ 檢索結果的第四列並沒有'的'或'之'兩個字,爲何該列會被選出 來呢?原因是Big5碼'中'(內碼A4A4)的low byte與'即'(內碼A759) 的high byte 恰巧合成'之'(內碼A4A7)這個字,因而產生誤判的情 形。 例二、執行grep ``[站立]'' test.doc,結果如下: ┌───────────────────┐ │蒐集相關資料。 │ │以正確的資訊做判斷。 │ │全文檢索滿足您的需要。 │ │之乎者也成功的中文化。 │ │在瀏灠畫面中即無法顯示出來。 │ │擁有全文檢索系統使您的工作更加有效率。 │ └───────────────────┘ 可以很清楚的看出test.doc內並不含'站'或'立'這兩個字,但卻 選出了六列資料。若資料量很大,要想去除這種中文字誤判的情形 ,可真是一件惱人的工作! 2.中文化的過程 GNU grep有完整的原始程式碼,並且在「GNU General Public License」的規範下,任何人皆可以自由的擁有、修改及傳播它。這 對於想要擁有一個好的中文化工具的我們,的確是一個好消息! GNU grep具備傳統UNIX指令grep家族之功能,且幾乎完全相容。 其與grep家族之對應如【表三】所示: 表三 GNU grep 與 UNIX grep家族 ┌────┬───────┬───────────┐ │GNU grep│UNIX grep家族│regular expression │ ├────┼───────┼───────────┤ │grep(-G)│grep │standard regular exp. │ ├────┼───────┼───────────┤ │grep -E │egrep │extended regular exp. │ ├────┼───────┼───────────┤ │grep -F │fgrep │不支援regular exp. │ └────┴───────┴───────────┘ GNU grep 使用三種字串搜尋方法(matcher),分別爲KWS matcher 、DFA matcher及regex matcher。對於完整字串(注三)所構成的樣 式(pattern),使用KWS matcher 即可完成檢索的工作。而 Regular expression 所構成的樣式,若不含Back-reference 運算子(,-), 使用KWS matcher及DFA matcher即可完成檢索工作;若含有 Back-reference運算子,則須再使用regex matcher方可完成檢索的 工作。 【表四】列出GNU grep使用各matcher的情形。 表四 GNU grep使用的檢索方法 ┌─────┬────────────┐ │GNU grep │ 檢索方法 │ ├─────┼────────────┤ │grep(-G) │ KWS、DFA、Regex matcher│ ├─────┼────────────┤ │grep -E │ KWS、DFA、Regex matcher│ ├─────┼────────────┤ │grep -F │KWS matcher │ └─────┴────────────┘ 由於regex matcher的檢索速度很慢,且DFA matcher已含蓋KWS matcher的檢索功能,故我們只針對DFA matcher進行中文化(不考 慮Back-reference運算子)。 DFA matcher的理論架構即是 Compilers 書上所提的「確定性有限自動機」(Deterministic Finite Automata),該方法效率佳,但須耗用較多的記憶空間。 GNU grep的原始程式使用bit map 的方式來建立狀態移轉表 (transition table),這對處理1byte字元(0-255)是一個效率很好 的方法。不過若用這個方法爲數量龐大的中文字(Big5 2bytes code)建立狀態移轉表,大量的記憶空間需求,並不是一般電腦所能 負荷的。因此我們以集合的觀念(交集、餘集、差集、...)改寫bit map的方法,使得記憶空間維持在可接受的狀況。當然效率與空間是 一個很難取捨的問題,新的方法執行速度雖然比不上原來的方法, 但仍不致於太差。 要能做到徹底的中文化,中文字與其它字元(例如:ascii或 0-255)都必須以一個完整字元的觀念來處理,而不是將Big5中文碼 拆成二個ascii code分別處理。這一點在對 GNU grep 進行中文化 的過程皆已考慮到,也因此regular expression中的'. '可以代表 一個英文字或一個Big5中文字;'資〔訊料源〕'可以找到「資訊」 、「資料」或「資源」等詞,不再有受騙的恐懼了。 四、效能測試 要知道一個工具的性能好壞,一份完整的測試報告是最具說服力 的。以下就是我們對中文化的GNU grep所做的效能測試(測試環境 爲SUN SPARCCenter 2000上執行SunOS 5.4),並與未中文化的GNU grep及 UNIX 上的 egrep 作一比較。 (中文化後的GNU grep我們以 cgrep來命名)。 表五 測試時所使用的樣式(pattern) ┌───┬────────────────────────────────┐ │ │ pattern(s) #│ ├───┼────────────────────────────────┤ │pat. 1│ window 1│ ├───┼────────────────────────────────┤ │pat. 2│window|/<the/>|pat.*n|disk|easy 5│ ├───┼────────────────────────────────┤ │pat. 3│window|/<the/>|pat.*n|disk|easy|grand|happy|mouth|noun|open 10│ ├───┼────────────────────────────────┤ │pat. 4│window|/<the/>|pat.*n|disk|easy|grand|happy|mouth|noun|open| │ │ │quit|quiz|rich|string|teach|limit|vesa|wear|xray|year 20│ ├───┼────────────────────────────────┤ │pat. 5│window|/<the/>|pat.*n|disk|easy|grand|happy|mouth|noun|open| │ │ │quit|quiz|rich|string|teach|limit|vesa|wear|xray|year|zone| │ │ │zoo|bolck|blank|people 25│ ├───┼────────────────────────────────┤ │pat. 6│window|/<the/>|pat.*n|disk|easy|grand|happy|mouth|noun|open| │ │ │quit|quiz|rich|string|teach|limit|vesa|wear|xray|year|zone| │ │ │zoo|bolck|blank|people|monney|splin|shift|pause|scroll| │ │ │enter|custom|friend|share|close|match|philo|phy|compact| │ │ │stock|head|trail|hair|eye 60│ └───┴────────────────────────────────┘ 【表五】爲測試時所使用的樣式(pat.1-6),其中第三欄(#)內的 數字表示該樣式是由幾個子樣式(subpattern)所組成。 【表六】內 之數字爲程式執行時所花費的實際時間(real time;單位:秒)。若 將測試結果以曲線圖表示,可以更清楚的看出egrep、 grep -E 及 cgrep -E之執行效率。如【圖一】所示,可以很清楚的看出,egrep 之時間曲線幾乎爲指數成長,而grep -E與cgrep -E 則爲線性成長 ,且曲線有漸趨平緩的傾向。 表六 egrep、grep -E、cgrep -E 測試結果 ┌────┬───┬───┬───┬───┬───┬───┐ │#---> │pat. 1│pat. 2│pat. 3│pat. 4│pat. 5│pat.6 │ ├────┼───┼───┼───┼───┼───┼───┤ │ │ 1 │ 5 │ 10 │ 20 │ 25 │ 60 │ ├────┼───┼───┼───┼───┼───┼───┤ │egrep │ 4.4 │ 4.6 │ 5.0 │ 22.2 │ 39.8 │ 467.7│ ├────┼───┼───┼───┼───┼───┼───┤ │grep -E│ 0.6 │ 3.0 │ 3.0 │ 3.3 │ 3.7 │ 7.9│ ├────┼───┼───┼───┼───┼───┼───┤ │cgrep -E│ 9.7 │10.0 │ 15.0 │ 17.0 │ 18.2 │ 20.9│ └────┴───┴───┴───┴───┴───┴───┘ 圖一 egrep、grep -E、cgrep -E之曲線圖 五、結語 由計算中心開發完成的「中文全文檢索資料庫」目前已有文字版 及WWW(Word Wide Web)版本,使用者已遍及世界各學術領域。爲了 讓使用者能更靈活的使用這個資料庫,我們已着手進行加入 wildcard字元(*、?)的檢索功能到這個資料庫內。 檢索時,要完成單一樣式(pattern)結合wildcard字元的檢索功 能可以輕易的做到,不過結合多個樣式(multipattern)與wildcard 字元的檢索功能,若要維持高效率的檢索能力,仍得多下點功夫。 欲達到多個樣式(multipattern)與wildcard字元的檢索功能, regular expression是直覺可以運用的好方法,它在UNIX 上已被 廣泛運用(例如:grep、egrep、awk、sed、vi、emacs,...),並且 可能已有完整的C Library可供使用(注四)。 我們查閱許多演算法相關書籍,並希望能找到UNIX上結合 multipattern與regular expression的檢索工具---egrep所使用的 演算法。在這個過程中測試了egrep、agrep(注五)及GNU grep的功 能與執行效率,並閱讀agrep 及GNU grep的部份程式碼。評估後, 由於GNU grep有完整的程式,並已支援extended regular expression(.、*、|、...),故選定GNU grep有關regular expression之相關程式作深入瞭解,進而完成DFA module之中文化 ,這就是cgrep中文化的由來。 cgrep是在強化「中文全文檢索資料庫」功能的途中,所獲得的附 加成果,相信對許多人的工作會有所幫助,因此先提出來與各位分 享!不過,截至目前爲止,要完成wildcard 字元與multipattern 的全文檢索功能,仍有許多技術上的瓶頸等待突破,期待能早日提 供各位一個更完整的「中文全文檢索資料庫」。 注 目前支援regular expression的工具,通常只處理ASCII字元 (0-127)組成的regular expression,頂多也只擴充到處理字元 0-255(即8bits字元,或稱1byte字元)。至於中文字的處理,有些工 具並不支援,或者只支援由EUC(Extended UNIX Code)構成的中文 字。對於使用最廣泛的Big5中文碼--- 2bytes字元,現有工具上之 regular expression並無法正確的表示。 理想上應該只有一個grep指令,而非grep家族(grep、egrep、 fgrep),以減少使用者因記憶太多指令而造成混淆或誤用的情況。 然而權衡效率與記憶空間使用的情形,要找到一個既節省空間且有 效率的演算法,實在不是一件容易辦到的事。 grep家族即是使用不 同的演算法(DFA、NFA、...)分別完成,以確保其能有較佳的執行表 現。 完整字串是由不含'*'運算子的regular expression所構成。以 實例來說明完整字串的含義:字串``happy''、``the|之| people''皆爲完整字串所構成的樣式(pattern);而字串``hapy''、 ``[Tt]he|peoe|〔之的〕''則不是由完整字串所構成的樣式。 UNIX C 上有幾組處理regular expression的library function ,例如:【compile(),step()】、【recomp(),reexec()】。經研 讀其man page 及實際測試後,發現它們只支援standard regular expression而不支援extended regular expression,故無法運用於 muliti-pattern之檢索需求。 agrep爲中正大學吳升(Sun Wu)老師的作品。它除了擁有 grep 的 功能外,並提供許多新的功能,例如:容錯搜尋及樣式間的邏輯運 算(AND、OR),不過仍有許多使用上的限制。另外,吳老師有一項新 作品GAIS(Global Area Information Servers),是一個多用途的資 訊搜尋系統,有興趣的讀者可以使用WWW Browser連至 http://gais.cs.ccu.edu.tw,以獲得更詳細的訊息。 (在進行GNU grep 中文化的過程中,感謝工作站袁天竑、陳弘哲 兩位同仁熱心協助,提供最新的程式(source code)及技術手冊,同 時,更感謝林晰先生細心的指導,使得本文得順利完成)。
Regular expression 與 GNU grep 的中文化
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.