在解决问题之前,我们需要了解一些关于‘零宽断言’特性
⑴ 断言(锚点也一样)和一般的正则表达式符号不同,它不匹配实际的任何字符,而是寻找文本的中的位置,是0长度。
他们匹配的是字符之前或之后的位置。如$并不是匹配换行符,而是匹配目标字符串的末尾或整个字符串末尾的换行符之前的位置
(?=pattern),(?<=pattern),(?!Pattern),(?<!pattern) ^,$,\b,\A,\Z 都是这样的只找‘位置’的元字符
⑵ 对于断言来说,明白两个重要概念:‘当前位置’和‘不消耗字符’,非常重要两条,如(?<=Auto)(?=It)和(?=It)(?<=Auto)是等价的,你能解释吗?
$sResA = StringRegExpReplace("AutoIt","(?<=Auto)(?=It)","'")
$sResB = StringRegExpReplace("AutoIt","(?=It)(?<=Auto)","'")
你打印下会发现,$sResA和$sResB的结果是一样。若你能解释为什么一样的,说明你理解所谓的‘断言’已经有一定深度了。
(?<=Auto)表示当前位置的左边是‘Auto’,而(?=It)表示当前位置右边是‘It',也就是只要是断言,匹配了断言的子表达式之后的‘当前位置’
跟匹配之前的‘当前位置’是同一个位置,这也就是当引擎匹配完断言的子表达式之后,不会消耗掉如上面‘It’两个字符的,
下一次匹配还是这Auto的o后面开始。
这也是为什么象表达式:foo(?=bar)bee 无论匹配什么字串,它的结果永远为空的原因,因为无论在断言子表达式匹配前还是匹配后,
引擎的当前查找位置都在foo后面,这时已经匹配了bar了,而该表达式又想在foo后面匹配bee,那就不可能了。此时到可以这样匹配
foo(?=bar)bar,虽然可以匹配,但不过是画蛇添足而已
好了,理解了上面基础的两点,我们就可以来做这道题了
① 第一步,我们先来个稍简单的,让:
$sStr = "1234567890" 也就是字符串只有一个纯多位数字
这个问题核心就是从数字的最后开始三位三位地数,然后添加逗号,也就是逆序(从右到左),最容易想到的当然是逆序断言了(方向一至嘛)
于是就有两个思路,一则就是纯只找符合要求的位置,二则就是找符合要求的数字后面,先看第一个思路:
那么从末尾向右数3位3位地数,用正则很容易想到: ((\d{3})+$),用$来定位串末尾的位置;再考虑不能在第一位数字前给加上逗号了,于是我们用
逆序肯定断言 (?<=\d) 来保证(这个表达式的意思就是要找的‘位置’前面不能是数字),这个也容易。因为这里是用的纯找位置的方法,而表达式
((\d{3})+$) 在匹配时是要消耗字符的,跟思路不合,于是限定一下,马上想到用顺序肯定断言来限制,((\d{3})+$)于是就变成了 (?=((\d{3})+$))
现在把二者联系起来,不就可以了