(转)为何须要正则表达式 by 王珢

为何须要正则表达式

by 王垠正则表达式

学习Unix最开头,你们都学过正则表达式(regexp)。但是有没有人考虑过咱们为何须要正则表达式?安全

正则表达式原本的初衷是用来从无结构的字符串中提取信息,却不知这正好是Unix的缺陷所在。Unix用无结构的字符串来表示数据,致使了诸多复杂的基于regexp的软件的诞生。sed, AWK, Perl, … 都是为了一样的目的来到这个世界上的。若是不是由于Unix用字符串来表示数据,咱们就会拥有按数据结构类型的直接存储,而不须要折腾regexp。正则表达式有它本身的价值(针对天然语言),可是咱们其实不须要把它应用到程序语言和操做系统里面。数据结构

正则表达式自己用一个字符串来表示,这带来另一些问题。由于正则表达式的本质不是字符串,而是一个数据结构。学过计算理论的人可能知道这个数据结构叫作NFA(nondeterministic finite automaton,非肯定性有限自动机)。全部的数据结构应该由程序语言自己来表示,就像用Java构造一个对象用 new ClassA("a") 同样。可是正则表达式强迫你把这个简单的构造函数调用写成一个字符串。因此在这个比方之下,你得写成new ClassA(\"a\")。这样当你想要组合这些表达式的时候就发现,正则表达式几乎都是不可组合(compose)的。你几乎不可能不能把两个regexp的变量A和B安全拼接成一个,好比用Java的字符串拼接A+B。由于你不知道这两个字符串拼在一块儿以后,那些稀奇古怪的符号会出现什么交叉反应,使得最后的识别的东西根本不是你想要的。函数

在正则表达式中,因为正则表达式自己的构造函数与数据自己合并到一块儿,咱们不得不对某些“特殊字符”进行escape。这些特殊字符,实际上是用来描述NFA的记号,它们属于更高一层的语言。但是在正则表达式里,它们与NFA节点里的字符混为一谈。好比很简单的一个block comment的正则表达式,却要写成这个样子:post

/\\*([^\\*]|[^/])*\\*/

显然这样的表达式很容易出错。 若是咱们用程序语言的表达式来构造这个表达式,它应该是这样:学习

(@... "/*" (@*(@!"*/")) "*/" )

在这个我本身设计的Scheme表达式里,以@开头的标识符都是构造函数。其中@...是构造sequence,@* 是构造一个zero-or-more的匹配,@!构造一个否认匹配。这个表达式是说:“以/ *开头,接着零个或者多个不是* /的字符,最后接着一个* /。这样一来清晰明了,什么表达式在什么“层次”都很清楚,不须要什么反斜杠escape,并且这样的表达式能够compose。好比:操作系统

(define reg1 (@... "/*" (@*(@!"*/")) "*/" ))
(define reg2 (@+ "foo"))
(define reg3 (@= "b"))

定义这三个表达式以后,咱们以后能够用像(@... reg1 (@or reg2 reg3)) 这样的表达式来链接3个不一样的表达式,构造出更大的表达式。这样的构造能够无限的扩展。从这里以及以往的经验,我总结出一个广泛适用的程序设计的教训:尽可能不要把多个层次的语言“压缩”到一层。咱们也看到正则表达式与“Unix哲学”有很大关系。我没有考古,因此不知道孰先孰后,可是它们确定有直接的因果关系。二者都是Unix复杂性的来源。设计


This article was posted at yinwang’s sina blog,
on 2012-05-17.
Though it’s not available now.code

相关文章
相关标签/搜索