正则表达式的环视和匹配

前端之家收集整理的这篇文章主要介绍了正则表达式的环视和匹配前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。


环视

  环视匹配的最终结果就是一个位置,有四种类型的环视:

  (?=Expression) 顺序肯定环视,表示所在位置右侧能够匹配Expression

  (?!Expression) 顺序否定环视,表示所在位置右侧不能匹配Expression

  (?<=Expression) 逆序肯定环视,表示所在位置左侧能够匹配Expression

  (?<!Expression) 逆序否定环视,表示所在位置左侧不能匹配Expression


  可以用以下两个正则表达式理解环视:

  (1)字母、数字、特殊符号全部出现,至少8位

Patternpattern=Pattern.compile("^(?=.*[\\d]+)(?=.*[a-zA-Z]+)(?=.*[#$_!]+)[\\w#$!]{8,}$");
Matchermatcher=pattern.matcher("123456a890#");
while(matcher.find()){
System.out.println(matcher.group());
}

  表达式的意思是开头位置应该是这样一个位置,后面有数字、字母、特殊符号,然后后面有至少8个指定的字符。

 (2)字母、数字、特殊符号至少出现两种,6-20位

Patternpattern=Pattern.compile("^(?![\\d]+$)(?![a-zA-Z]+$)(?![!#$%^&*]+$)[\\da-zA-Z!#$%^&*]{6,20}$");
Matchermatcher=pattern.matcher("aaaaaaaaaaa!");
while(matcher.find()){
System.out.println(matcher.group());
}

  表达式的意思是开头和结尾中间不能全是数字,不能全是字母,不能全是特殊符号,然后是指定的字符出现6到20次。


三种匹配方式

  匹配优先(贪婪匹配)

  忽略优先(懒惰匹配)

  占有优先


  理解这三种方式的关键在于理解回溯,下面的代码辅助理解这三种方式:

publicstaticvoidmain(String[]args)
{
Patternpattern=Pattern.compile("a.*a");
Matchermatcher=pattern.matcher("ababa");
if(matcher.find())
{
System.out.println(matcher.group());
}
else
{
System.out.println("不匹配");
}

pattern=Pattern.compile("a.*?a");
matcher=pattern.matcher("ababa");
if(matcher.find())
{
System.out.println(matcher.group());
}
else
{
System.out.println("不匹配");
}

pattern=Pattern.compile("a.*+a");
matcher=pattern.matcher("ababa");
if(matcher.find())
{
System.out.println(matcher.group());
}
else
{
System.out.println("不匹配");
}
}

输出

ababa
aba
不匹配


固化分组和占有匹配

  下面的文字和例子取值《精通正则表达式》

  假设我们有这样的问题,把类似 3.690000023 的小数保留两位小数,类似 2.3563895 的小数保留三位小数,也就是说如果小数的第三位是0,则保留两位小数,如果是非0,就保留三位小数。

$number=~s/(\.\d\d[1-9]?)\d*/$1/;

  这个表达式完全可以工作,美中不足的一点是,当 $number 类似 3.695 的时候,我们把 .695 替换为了 .695,浪费了工夫。为了解决这个问题,我们把表达式稍稍修改一下。

$number=~s/(\.\d\d[1-9]?)\d+/$1/;

  仅仅把星号替换成了加号,这样表示括号外至少有一位数字的时候才进行替换。看上去很完美,但是却出现了致命的错误,.695 被替换成了 .69了。这是为什么呢?在表达式 (\.\d\d[1-9]?) 匹配了 .695 后, 后面的 \d+ 无法匹配了,为了使整个表达式匹配成功,引擎必须回溯,[1-9]?必须把匹配的数字吐出去,所以 5 被 \d+ 匹配了。这就是回溯造成的问题。事实上在这种情况下,我们不希望引擎回溯,有两种办法可以强迫引擎放弃回溯,固化分组和占有量词。

$number=~s/(\.\d\d(?>[1-9]?))\d+/$1/;#固化分组

$number=~s/(\.\d\d[1-9]?+)\d+/$1/;#占有量词

  引擎放弃回溯后, 上面的表达式将无法匹配 .695,这正是我们想要的。

  占有优先量词与匹配优先量词很相似,只是它们从来不交还已经匹配的字符。

  你也许会想,占有优先量词和固化分组关系非常紧密。像「\w++」这样的占有优先量词与「(?>\w+)」的匹配结果完全相同,只是写起来更加方便而已。使用占有优先量词,「(\.\d\d(?>[1-9]?))\d+」写做「(\.\d\d[1-9]?+)^\d+」。

  请务必区分「(?>M)+」和「(?M+)」。前者放弃「M」创建的备用状态,因为「M」不会制造任何状态,所以这样做没什么价值。而后者放弃「M+」创造的未使用状态,这样做显然有意义。

  比较「(?>M)+」和「(?>M+)」,显然后者就对应于「M++」,但如果表达式很复杂,例如

(\\"|[^"])*+

  从占有优先量词转换为固化分组时,大家往往会想到在括号中添加‘?>’得到 (?>\\"|[^"])*。这个表达式或许有机会实现你的目的,但它显然不等于那个使用占有优先量词的表达式;它就好像是把「M++」写作「(?>M)+」一样。正确的办法是,去掉表示占有优先的加号,用固化分组把余下的部分包括起来:

(?>(\\"|[^"])*)

  上面的过程可以使用下面的代码验证:

publicstaticvoidmain(String[]args)
{
String[]datas={"1.234001","1.234","1.230","1.23"};
Patternpattern=Pattern.compile("(\\.\\d\\d[1-9]?)\\d*");
for(Stringdata:datas)
{
Matchermatcher=pattern.matcher(data);
if(matcher.find())
{
System.out.println(matcher.group(1));
}
else
{
System.out.println("不匹配");
}
}

System.out.println("========");

pattern=Pattern.compile("(\\.\\d\\d[1-9]?)\\d+");
for(Stringdata:datas)
{
Matchermatcher=pattern.matcher(data);
if(matcher.find())
{
System.out.println(matcher.group(1));
}
else
{
System.out.println("不匹配");
}
}

System.out.println("========");

pattern=Pattern.compile("(\\.\\d\\d(?>[1-9]?))\\d+");
for(Stringdata:datas)
{
Matchermatcher=pattern.matcher(data);
if(matcher.find())
{
System.out.println(matcher.group(1));
}
else
{
System.out.println("不匹配");
}
}

System.out.println("========");

pattern=Pattern.compile("(\\.\\d\\d(?>[1-9]?+))\\d+");
for(Stringdata:datas)
{
Matchermatcher=pattern.matcher(data);
if(matcher.find())
{
System.out.println(matcher.group(1));
}
else
{
System.out.println("不匹配");
}
}
}

输出

.234
.234
.23
.23
========
.234
.23
.23
不匹配
========
.234
不匹配
.23
不匹配
========
.234
不匹配
.23
不匹配


其他参考资料

《[精华] 正则表达式30分钟入门教程》

http://www.oschina.net/question/12_9507


《千里之行始于足下》的博客

http://www.jb51.cc/cata/106952


《雁过无痕》的博客

http://www.jb51.cc/cata/402158

猜你在找的正则表达式相关文章