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

本文详细介绍了正则表达式中的Lookaround特性,包括正向预查与负向预查等四种类型,并通过实例展示了如何利用这些特性高效地处理字符串。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

正则表达式--- 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中的36了,这离123456789已经非常接近了。
我们需要将3替换为36替换为6,所以我们需要将36捕获到:"(/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、付费专栏及课程。

余额充值