正则表达式_lookAround通配符[转载]

正则表达式 --- Look around
在正则表达式中有一组特殊的通配符 ---- Look around 通配符,其包含四个子类通配符:
Table 2. Four Types of Look around
Type
Regex
Successful if the enclosed subexpression . . .
Positive lookbehind
Negative Lookbehind
(?<=......)
(?<!......)
successful if can match to the left
successful if can not match to the left
Positive Lookahead
Negative Lookahead
(?=......)
(?!......)
successful if can match to the right
successful if can not match to the right
在一般的资料中我们经常可以看见 Lookahead 的介绍:
(?=pattern)
正向预查,在任何匹配 pattern 的字符串开始处匹配查找字符串。这是一个 非获取匹配 ,也就是说,该匹配不需要获取供以后使用。例如, 'Windows (?=95|98|NT|2000)' 能匹配 "Windows 2000" 中的 "Windows" ,但不能匹配 "Windows 3.1" 中的 "Windows" 预查不消耗字符 ,也就是说,在一个匹配发生后,在最后一次匹配之后立即开始下一次匹配的搜索,而不是从包含预查的字符之后开始。
(?!pattern)
负向预查,在任何不匹配 pattern 的字符串开始处匹配查找字符串。这是一个非获取匹配,也就是说,该匹配不需要获取供以后使用。例如 'Windows (?!95|98|NT|2000)' 能匹配 "Windows 3.1" 中的 "Windows" ,但不能匹配 "Windows 2000" 中的 "Windows" 。预查不消耗字符,也就是说,在一个匹配发生后,在最后一次匹配之后立即开始下一次匹配的搜索,而不是从包含预查的字符之后开始
这比较详细的介绍了 Look ahead 通配符,从以上的一段描述中我们可以看见 Look ahead 通配符的特点:
1. 该通配符是一个非获取匹配;就是正则表达式引擎在处理该类通配符时不会把匹配项存储起来,不会供以后使用。
2. 该通配符是预查操作符,不消耗字符;这个可能不是很好理解,我们还是结合实例说明吧:比如有一串字符 a1b2c3 ,在匹配的开始,我们可以想象有一个指针,所指的位置在 a 的前面,我们用 \w 来匹配该字符串可匹配到 a ,然后下一个匹配应该从 1 之前的位置开始;但如果我们用 (?=) 来匹配时,我们匹配到 a, 可是这时候指针还是在 a 的前面;也就是说,指针的位置并没有改变。这就是所谓的 不消耗字符
以上介绍的 ”look ahead” 通配符,这两个通配符被解释为正向预查和负向预查,而 ”look behind” 的两个通配符我一直没有找到其对应的中文翻译,但是其性质和特点和 ”Look ahead” 非常相似,唯一的不同就是 ”look ahead” 是预查(向右), ” look behind” 复查? (向左)。
我们看看以上四个通配符都在什么情况下可以匹配;
string str23 = "abc9defg";
string strRegular26 = @"[a-z]{3}";
string strRegular27 = @"[a-z]{3}(?=\d)"; //lookahead
string strRegular271 = @"(?=\d)[a-z]{3}"; //lookahead
string strRegular28 = @"(?<=\d)[a-z]{3}"; //lookbehind
string strRegular281 = @"[a-z]{3}(?<=\d)"; //lookbehind
Console.WriteLine(Regex.Matches(str23,strRegular26).Count.ToString()); // 2
Console.WriteLine(Regex.Matches(str23,strRegular26)[0].Captures[0].ToString()); // abc
Console.WriteLine(Regex.Matches(str23,strRegular26)[1].Captures[0].ToString()); // def
Console.WriteLine(Regex.Matches(str23,strRegular27).Count.ToString()); // 1
Console.WriteLine(Regex.Matches(str23,strRegular27)[0].Captures[0].ToString()); // abc
Console.WriteLine(Regex.Matches(str23,strRegular271).Count.ToString()); // 0
// Console.WriteLine(Regex.Matches(str23,strRegular271)[0].Captures[0].ToString()); // 前向预查不消耗字符,所以这个匹配永远不可能出现
Console.WriteLine(Regex.Matches(str23,strRegular28).Count.ToString());//1
Console.WriteLine(Regex.Matches(str23,strRegular28)[0].Captures[0].ToString());//def
以上是一段.net环境下c#实现的代码,每句话的后面给出了输出结果,和常规表达式@"[a-z]{3}"相比较,在表达式变为: @"[a-z]{3}(?=\d)"时不匹配原有的 def 因为在向右看时, def 后面不是数字。在表达式变为@"(?<=\d)[a-z]{3}"后不匹配原有的 abc ,因为在向左看时, abc 前面没有数字。
让我们来看一段示例:
通过常用通配符和这四个通配符(不一定全用到)的结合,让我们试着将一串夹在字符串中的数字用千分号分隔开:
如:
The value is 123456789 dollars.
通过正则表达式变为:
The value is 123,456,789 dollars.
或者将:
The result is 123456789
通过正则表达式变为:
The result is 123,456,789
让我们分析这个问题:
首先分析我们应该如何分割 123456789
毫无疑问,我们应该从右边不是数字的字符或者位置开始,每隔 3 个数字且该位置之前还是数字(如 4 前面和 7 前面是应该找到了,而 1 前面不是应该匹配的。)
那么让我们逐步完善匹配正则表达式:
首先是匹配模式:在 .net 中应该用 RegexOptions.RightToLeft.
是一点首先明确,然后是左边不是数字,如果用的是 (?=[^0-9]+) 这已经很接近了,可是一个问题是不能匹配数字后什么都没有的东西了(因为用了一个 + )所以我们可以完善为 "(?=(\D|$))"
且右边有 3 个一组的数字一组或者多组 :"(?=(\d{3})+(\D|$) ",
而且左边至少有一位数字 :"\d(?=(\d{3})+(\D|$) " 这样我们通过这个正则表达式去匹配,我们就可以找到 123456789 中的 3 6 了,这离 123 456 789 已经非常接近了。
我们需要将 3 替换为 3 6 替换为 6 ,所以我们需要将 3 6 捕获到 :"(\d)(?=(\d{3})+(\D|$) "
这时候我们可以采用 Timmy 的方式用 Regex.Replace(strTest,strPattern,new atchEvaluator ReplaceNumber));
或者我们在正则表达式的替换中我们可以用 $1 来获取我们的第一组捕获,这样我们就可以得到一种解决方案:
string str37 = "123456789";
string strRegular47 = @"(\d)(?=(\d{3})+(\D|$))";
Console.WriteLine(Regex.Replace(str37,strRegular47,"$1,",RegexOptions.RightToLeft ));
这是一种替换 "3" -> "3," ; "6" -> "6," 的一种方式。这里我们就到一个 Positive Lookahead ,那么其实我们的实际操作就是希望在 "3" "6" 的位置后插入一个 "," ,我们可不可以只找出这个位置,然后替换为 "," 呢?
我想我们通过 (?<=......) (?<!......) 是可以达到只找到这个位置的目的的 :)
这里我也将表达式给出: (?<=\d+)(?=(\d{3})+(\D|$))
这样以上的表达式就只会匹配一个位置,而不对应任何字符 ,
string str37 = "123456789";
string strRegular47 = @"(?<=\d+)(?=(\d{3})+(\D|$))";
Console.WriteLine(Regex.Replace(str37,strRegular47,",",RegexOptions.RightToLeft ));
这样就可以不用捕获,也能完成插入 "," 了。
我们还可以对这个问题进行扩展,比如 123456789.123456 如果采用以上的方式处理就会获得 123,456,789.123,456
这显然不太合适的,我们应该获得的是 "123,456,789.123456" ,即小数点后不加以格式化。那么这个正则表达式又应该怎么写呢?
这还是需要大家多多考虑。
要熟悉正则,就是要多多练习,尤其是我们进行搜索的时候,还是很有用处的。:)
现对关于正则表达式中 look around 的总结如下:
1. 是非捕获匹配,不消耗字符。
2. look ahead ( 向右 ) look behind (向后)两种。其下又分正匹配和负匹配两种,共 4 种。
3. 属于特殊通配符,比较不容易通过其他通配符替代。
4 .合理运用可以大大提到字符处理效率。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值