《专题报导》
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 的中文化
最新推荐文章于 2025-11-17 15:15:09 发布
本文介绍了一款名为 cgrep 的工具,该工具通过改进 GNU grep 的 DFA 模块实现了对 Big5 中文编码的支持,能够准确地处理中文 regular expression,避免了传统 grep 工具在处理中文时出现的误判问题。
1394

被折叠的 条评论
为什么被折叠?



