基础
. 匹配除换行符之外的任意字符
\w [a-zA-Z0-9_]
\W [^a-zA-Z0-9_]
\d [0-9]
\D [^0-9]
\s 匹配任意一位空白符(\n,\t等)
\S 匹配任意一位非空白符
^ 匹配字符串的开始
$ 匹配字符串的结束
\b 匹配一个单词的边界,即字与空格间的位置。
\B 匹配非单词边界
单词边界是单词和空格之间的位置。非单词边界是任何其他位置。
下面的表达式匹配单词 Chapter 的开头三个字符,因为这三个字符出现在单词边界后面:\\bCha
\b 字符的位置是非常重要的。如果它位于要匹配的字符串的开始,它在单词的开始处查找匹配项。
如果它位于字符串的结尾,它在单词的结尾处查找匹配项。
例如,下面的表达式匹配单词 Chapter 中的字符串 ter,因为它出现在单词边界的前面:ter\\b
下面的表达式匹配 Chapter 中的字符串 apt,但不匹配 aptitude 中的字符串 apt:\\Bapt
字符串 apt 出现在单词 Chapter 中的非单词边界处,但出现在单词 aptitude 中的单词边界处。
对于 \B 非单词边界运算符,位置并不重要,因为匹配不关心究竟是单词的开头还是结尾。
* 任意次 贪婪
? 0,1 非贪婪
+ >=1 贪婪
{n} n
{n,} >=n 贪婪
{n,m} n~m 贪婪
在 *、+ 或 ? 限定符之后放置 ?,该表达式从"贪婪"表达式转换为"非贪婪"表达式或者最小匹配。
*? 任意次,尽可能少匹配
+? >=1次,尽可能少匹配
?? 0,1次,尽可能少匹配
*+ 任意次,尽可能多匹配
.*? 如:a.*?b匹配最短的,以a开始,以b结束的字符串。如果把它应用于aabab的话,它会匹配aab和ab。
进阶
断言
1.正向先行断言(正前瞻):
语法:(?=pattern)
作用:匹配pattern表达式的前面内容,不返回本身。
String reg="\\d+(?=</span>)";
String test = "<span class=\"read-count\">阅读数:641</span>";
Pattern pattern = Pattern.compile(reg);
Matcher mc=pattern.matcher(test);
while(mc.find()){
System.out.println(mc.group(0));//641
}
2.正向后行断言(正后顾)
语法:(?<=pattern)
作用:匹配pattern表达式的后面的内容,不返回本身。
String reg="(?<=<span class=\"read-count\">阅读数:)\\d+";
String test = "<span class=\"read-count\">阅读数:641</span>";
Pattern pattern = Pattern.compile(reg);
Matcher mc=pattern.matcher(test);
while(mc.find()){
System.out.println(mc.group(0));//641
}
3.负向先行断言(负前瞻)
语法:(?!pattern)
作用:匹配非pattern表达式的前面内容,不返回本身。
找到不是'的花朵'前面的祖国
String reg="祖国(?!的花朵)";
String test = "我爱祖国,我是祖国的花朵";
Pattern pattern = Pattern.compile(reg);
Matcher mc=pattern.matcher(test);
while(mc.find()){
System.out.println(mc.group());//祖国
}
捕获
捕获组:匹配子表达式的内容,把匹配结果保存到内存中中数字编号或显示命名的组里,以深度优先进行编号,之后可以通过序号或名称来使用这些匹配结果。
1.数字编号捕获组:
语法:(exp)
解释:从表达式左侧开始,每出现一个左括号和它对应的右括号之间的内容为一个分组,
在分组中,第0组为整个表达式,第一组开始为分组。
String test = "020-85653333";
String reg="(0\\d{2})-(\\d{8})";
Pattern pattern = Pattern.compile(reg);
Matcher mc= pattern.matcher(test);
if(mc.find()){
System.out.println("分组的个数有:"+mc.groupCount());
for(int i=0;i<=mc.groupCount();i++){
System.out.println("第"+i+"个分组为:"+mc.group(i));
}
}
分组的个数有:2
第0个分组为:020-85653333
第1个分组为:020
第2个分组为:85653333
2.命名编号捕获组:
语法:(?exp)
解释:分组的命名由表达式中的name指定
String test = "020-85653333";
String reg="(?<quhao>0\\d{2})-(?<haoma>\\d{8})";
Pattern pattern = Pattern.compile(reg);
Matcher mc= pattern.matcher(test);
if(mc.find()){
System.out.println("分组的个数有:"+mc.groupCount());
System.out.println(mc.group("quhao"));
System.out.println(mc.group("haoma"));
}
分组的个数有:2
020
85653333
3.非捕获组:
语法:(?:exp)
解释:和捕获组刚好相反,它用来标识那些不需要捕获的分组,说的通俗一点,就是你可以根据需要去保存你的分组。
String test = "020-85653333";
String reg="(?:\0\d{2})-(\d{8})";
Pattern pattern = Pattern.compile(reg);
Matcher mc= pattern.matcher(test);
if(mc.find()){
System.out.println("分组的个数有:"+mc.groupCount());
for(int i=0;i<=mc.groupCount();i++){
System.out.println("第"+i+"个分组为:"+mc.group(i));
}
}
分组的个数有:1
第0个分组为:020-85653333
第1个分组为:85653333
反向引用
反向引用需要使用到分组,分组就是使用()括起来的部分为一个整体,在进行分组匹配时的原则是:由外向内,由左向右
如:1,2等
\1:表示的是引用第一次匹配到的()括起来的部分
\2:表示的是引用第二次匹配到的()括起来的部分
1)例:
String regex = "^(\\d)\\1$";
首先这里是匹配两位,\d一位,\1又引用\d一位 这里的\1会去引用(\d)匹配到的内容,因为(\d)是第一次匹配到的内容。
如:str = "22"时,(\\d)匹配到2,所以\1引用(\\d)的值也为2,所以str="22"能匹配
str = "23"时,(\\d)匹配到2,因为\1引用(\\d)的值2,而这里是3,所以str="23"不能匹配
2)例:
String regex = "^(\\d)\\1[0-9](\d)\1{2}";
注意在后面第二个(\d)\1{2}中的\1,这里的\1并不会去匹配他前面的(\d),而是匹配第一个(\\d),
如:第一个(\d)为3时,则第一个\1也为3,同样最后那个\1也为3,所以结果可以是335933,332533,而不是336444,339888
3)例:
String regex = "^(\d)\1[0-9](\d)\2{2}$";
这里使用了\2引用第二次匹配到的分组,这里第二次匹配的分组为\2前面的(\d),这里的{2}指的是\2的值出现两次
如:第一个(\d)为4时,\1引用第1个(\d)也为4,第二个(\d)为5时,\2引用第二个(\d)为5,所以结果可以是:447555,440222
4)例:
String regex = "^((\\d)3)\\1[0-9](\\d)\2{2}$";
当匹配中的分组有嵌套时,是从外向里匹配的,其次在由左向右匹配
这里主要是分析匹配到分组的顺序,首先匹配((\d)3)这整个部分,其次匹配((\d)3)里面的(\d),第三次匹配时最后一个\2前面的(\d)
如:如((\d)3)中的(\d)为2时,((\d)3)的值为23,此时\1为((\d)3)的值1,而\2引用((\d)3)中的(\d)的值3,第三个(\d)为5时,
此时\3引用第三个(\d)的值5,所以结果可以有:23238522,23230522
部分信息来源于:
你是如何学会正则表达式的?
正则表达式中的反向引用