为什么正则表达式如此具争议性?[已关闭]
为什么正则表达式如此具有争议?
正则表达式是一种很好的工具,但人们会认为,“嘿,这是一个很好的工具,我会用它来做X!”这里的X指的是其他更适合的工具(通常是解析器)。这就像是在需要用螺丝刀的地方使用锤子的问题。
但请记住,大多数解析器(词法分析器)仍然使用正则表达式来解析它们的内容 🙂
说解析器使用正则表达式就像说解析器使用赋值语句一样。除非你查看它们的使用方式,否则这句话毫无意义。
当有更好的解析器可用时,使用正则表达式是很烦人的。当语言的标准字符串查找或替换函数可以工作(通常是线性时间),使用正则表达式就是不可原谅的。
同意,因为正则表达式必须具备多种功能,所以它的处理开销很大。仅仅因为使用正则表达式引擎看起来很容易,并不意味着它比迭代解析器更好(这取决于开发者的阈值)。我最喜欢的一个例子是PHP的split($pattern,$string)
与explode($delimiter,$string)
,幸运的是前者已经被弃用,但很多代码在只需要后者的功能时使用了前者。同意,正则表达式提供了一个简单的工具来做一些事情,但除非你需要正则表达式的全部功能,否则它们并不是最好的解决方案。
词法分析器可能确实使用正则表达式。它们也被称为标记器,但它们不是语法分析器(或解析器)。要读取一个足够复杂的字符串,应该使用标记器将字符串读取为标记(也许使用正则表达式,也许不使用,这取决于标记器)。然后将这些标记传递给解析器,解析器将使用语法规则处理它们,而这些规则绝对不是正则表达式。
为什么正则表达式如此具有争议?
正则表达式一直以来都被认为是一种神秘的模式,Perl的/x
正则表达式标志(有时嵌入时写作(?x)
)可以解决这个问题,它允许空格和注释,从而提高了可读性和可维护性。
现代的正则表达式模式现在支持相对编号和命名后向引用。这意味着您不再需要计算捕获组来确定您需要的是$4
还是\7
。这在创建可以包含在其他模式中的模式时很有帮助。
文章给出了一个相对编号捕获组的例子和一个命名捕获组的例子。相对编号捕获组的例子如下:
$dupword = qr{ \b (?: ( \w+ ) (?: \s+ \g{-1} )+ ) \b }xi;$quoted = qr{ ( ["'] ) $dupword \1 }x;
命名捕获组的例子如下:
$dupword = qr{ \b (?: (?<word> \w+ ) (?: \s+ \k<word> )+ ) \b }xi; $quoted = qr{ (?<quote> ["'] ) $dupword \g{quote} }x;
除此之外,正则表达式还可以像子例程一样在模式中分离出声明和执行。这种“语法正则表达式”可以使模式看起来更像一个语法声明。现代的语法模式允许直接将BNF风格的语法转换为代码,而不会丢失其基本结构。
如果现代的语法模式仍然无法满足需求,那么Damian Conway的Regexp::Grammars模块提供了一个更干净的语法,而且还提供了更好的调试功能。
现代的正则表达式设计特性的改进不仅仅局限于Perl。其他语言如Python也有类似的特性。
现代的正则表达式模式与您在有限自动机课程中学到的原始模式几乎没有任何共同之处。正则表达式可以是可读的,只需要一些技巧和良好的编程实践。
为什么正则表达式如此具有争议性?
正则表达式具有争议,主要是因为它们难以阅读和编写,并且很难正确使用。尽管在某些情况下,正则表达式可以提供有效且简洁的解决方案,但有时候它们被强行用于本应使用易于阅读和可维护的代码的情况。
是的,与使用简单函数相比,正则表达式可能非常非常慢。而且不仅慢,当面对任意(用户提供的)输入时,正则表达式引擎的性能也可能是完全不可预测的。
如果你了解正则表达式的工作原理,这就不是问题。
慢的不是正则表达式的模式,而是正则表达式引擎。大多数(现代的)正则表达式引擎对于复杂的模式(例如许多|或.*)是不适用的,因为它们使用了堆栈机和回溯。这就是为什么你必须在Perl、Java、Python、Ruby等语言中仔细调整你的正则表达式。旧式的正则表达式引擎(例如grep)首先将模式编译为DFA。之后,模式的复杂性在很大程度上是无关紧要的。我刚刚在相同的文本和模式上使用了Java和grep:22分钟 vs 2秒。这是科学依据:swtch.com/~rsc/regexp/regexp1.html
需要注意的是,在许多情况下,特别是在“一次性使用”的情况下,最好使用现成的基于正则表达式的工具(例如众所周知的UNIX工具)而不是编写实际的“程序”,这样就需要安装适当的开发环境,了解所使用的编程语言等。