环视
环视匹配的最终结果就是一个位置,有四种类型的环视:
(?=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
《雁过无痕》的博客