在准备内部的正则表达式 (Regular Expression)分享的过程中, 注意到一个细节问题:重复的子模式,是应该前倾还是后倾,这两者在性能上有什么差异?
前倾和后倾是我胡诌的两个词,我的定义如下:
前倾 就是前面的子表达式使用贪婪匹配或较大的量词,尽可能多的匹配内容, 导致前面匹配的内容较多而使“天平”前倾。例如:
/(?:\d{1,3}\.){3}\d{1,3}/
。
后倾 则相反,后面的子表达式使用贪婪匹配或较大的量词。如:/\d{1,3}(?:\.\d{1,3}){3}/
。
前倾和后倾在实际应用中很常见,如时间,主机名等,中间以某特殊字符(串?)分隔, 被分隔部分的规则又相同的情况下,一般都是使用这两种实现方式之一。
上面定义中的两个例子只是简例,可以用来大致匹配 IPv4 地址,完整的 IPv4 地址正则式如下, 它要求每段都在 0-255 之间: 前倾:
/^(?:(?:[0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}(?:[0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$/
1
后倾:
/^(?:[0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(?:\.(?:[0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])){3}$/
1
我写了一个测试比较这两个正则性能 的页面,在 IE7, FF3.6, Safari 4, Opera 10 下测试均为前倾的性能较高,Chrome 4 下则不相伯仲。
p.s. 这种重复子模式,要有语法上的简写就好了,像 Perl 正则的递归引用(或类似运行时反向引用), 维护起来就方便多了。