正则中用到的字符,不加转义正则会解析成特殊含义。需要转义的即在正则中代表特殊含义的字符。如下:
^、$、.、*、+、?、|、\、(、)、[、]、{、}、=、!、:、-
若要匹配以上字符在java中必须用\进行转义,否则代表特殊含义。比如String中的split方法,以点分隔。split("\\.")
横向模糊匹配:一个正则表达式匹配的字符串长度是不固定的,比如?,*,+,{m,n}等。
例如:ab{2,4}c。匹配的字符有(注意红色字体,很好的体现了横向这个词):
abbbc
abbbbc
abbbbbc
纵向模糊匹配:一个正则匹配的字符串,在匹配的位不确定位的值,比如\d,[a-z],\w等。
例如:a[123]b。匹配的有(注意红色字体,很好的体现了纵向这个词):
a1c
a2c
a3c
给小括号分等级。先找到最外的小括号然后找它的孩子、孙子直至找完,然后找兄弟,兄弟的孩子、孙子直至找完。依次类推就能正确的计算分组,这是使用正向引用、反向引用分组的前提。分组是括号中匹配到的内容。
比如正则((\d)(\d(\d)))(a)。先找到一对小括号,((\d)(\d(\d)))为分组1,即三个数字。接着找它的孩子,即(\d)、(\d(\d))分别为分组2,分组3。然后找分组3的孩子(\d)为分组4。找完之后,然后找它的兄弟,即(\a)为分组5。
如:字符串为123a。正则为上述正则。
分组1为:123
分组2为:1
分组3为:23
分组4为:3
分组5为:a
使用\数字进行引用分组。比如引用分组\1。即把数字给转义代表引用的分组。正向引用是在正则表达式中引用,而反向引用是在匹配后的结果中引用。
例如匹配日期支持以下三种形式:
2020-06-20
2020/06/20
2020.06.20
想下正则:按照先局部再整体的原则
匹配年份:(\d){4}
匹配格式符号:([\\-\\./])
匹配月份:(\d){2}
匹配天数:(\d){2}
整体的正则为:(\d){4}([-./])(\d){2}([-./])(\d){2}
本以为万事大吉,但是能匹配到2020-06.20等即没控制格式符号。导致不符合正则的要求。
使用分组正则表达式改为:(\d){4}([\\-\\./])(\d){2}(\2)(\d){2}
private static void regexReference() {
String date = "2020-06-20";
String date1 = "2020/06/20";
String date2 = "2020.06.20";
String date3 = "2020-06.20";
String regex = "(\\d){4}([\\-\\./])(\\d){2}([\\-\\./])(\\d){2}";
System.out.println(Pattern.compile(regex).matcher(date).find());
System.out.println(Pattern.compile(regex).matcher(date1).find());
System.out.println(Pattern.compile(regex).matcher(date2).find());
System.out.println(Pattern.compile(regex).matcher(date3).find());
//匹配格式符号是分组2,所以引用的是分组2
String regexReference = "(\\d){4}([\\-\\./])(\\d){2}(\\2)(\\d){2}";
System.out.println(Pattern.compile(regexReference).matcher(date).find());
System.out.println(Pattern.compile(regexReference).matcher(date1).find());
System.out.println(Pattern.compile(regexReference).matcher(date2).find());
System.out.println(Pattern.compile(regexReference).matcher(date3).find());
//关于不引用分组,加了(?:)所以引用分组改成了引用分组1
String regexReference1 = "(?:\\d){4}([\\-\\./])(\\d){2}(\\1)(\\d){2}";
System.out.println(Pattern.compile(regexReference).matcher(date).find());
System.out.println(Pattern.compile(regexReference).matcher(date1).find());
System.out.println(Pattern.compile(regexReference).matcher(date2).find());
System.out.println(Pattern.compile(regexReference).matcher(date3).find());
}
使用$数字来反向引用分组,反向引用分组是对结果的引用。
比如sql模糊查询的时候,输入的字符串中有%_需要进行其转义。在mysql中也是使用%进行转义。只有模糊查询的时候需要转义。
String str = “%hel_”;
正则:[%_]
\就要用\\表示因为第二个反斜线在第二个参数中有特殊意义
str.replaceAll("([%])","\\\\$1")
输出为:**\%hel\**
注意:
分组后面有量词,所匹配的分组是最后一个满足的字符。
比如字符串:ab90。正则([ab90])。那么匹配的分组为0。
若想引用所用的加括号,正则改为(([ab90]))。
所谓贪婪量词即尽可能匹配多的字符满足正则表达式,所谓惰性量词即尽可能匹配少的字符满足正则表达式。
贪婪匹配的量词有:?,*,+,{m,n}
惰性匹配:在贪婪量词后加?即为惰性匹配。
惰性匹配的量词有:??,*?,+?,{m,n}?,还有分支结构**|**
private static void greedAndInertia(){
String str = "abcccde";
String greedRegex = "abc+";
Pattern greedPattern = Pattern.compile(greedRegex);
Matcher greedMatcher = greedPattern.matcher(str);
while (greedMatcher.find()) {
System.out.println("贪婪匹配到的字符串为:");
System.out.println(greedMatcher.group());//输出结果为:abccc
}
//惰性匹配
String inertiaRegex = "abc+?";
Pattern inertiaPattern = Pattern.compile(inertiaRegex);
Matcher inertiaMatcher = inertiaPattern.matcher(str);
while (inertiaMatcher.find()) {
System.out.println("惰性匹配到的字符串为:");
System.out.println(inertiaMatcher.group());//输出结果为:abc
}
}
注意: 分支结构"|"。
String str = "foohefoot";
String regex = "foo|foot";
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(str);
while(matcher.find()){
System.out.println(matcher.group());
}
输出结果不是我们想要的foo和foot。两次的输出结果都是foo。
解决办法:
总结:无论贪婪匹配、惰性匹配都是在满足正则的基础上更多或更少的匹配字符。
忽略大小写:(?i)
例如:(?i)abc。正则的含义为匹配abc且忽略大小写。
(?i)的作用范围在之后的所有字符(若没有括号)。若有括号作用范围就在括号内,包括子括号。
比如匹配abc其中忽略bc大小写。正则为:a(?i)bc
比如匹配abc其中忽略b大小写。正则为:a((?i)b)c
比如:a((?i)b(d)),代表的含义是匹配abd且bd忽略大小写。
区间内去除:[区间&&[^去除的字符]]
比如:匹配字符a-f但不包含ce。正则为:[a-f&&[^ce]]。
什么是位置,也称为锚。字符相邻之间都有位置。比如字符串hello。箭头所示就是位置。
hello的等价形式可写成。""即是位置。与图作为呼应。
“hello” == “” + “h” + “” + “e” + “” + “l” + “” + “l” + “o” + “”;
匹配位置的一共有八个:^、$、\b、\B、(?=p)、(?!p)、(?<=p)、((?。后四种叫做断言或是环视。在匹配的时候可以想象成等价的形式,看匹配的是哪个位置。
比如 字符串"for example he is wise man"。想在这句话的后面加me too。是在后面的位置加。所以使用(?<=P)。正则为:(?<=he is wise man)。
String str = "for example he is wise man";
String regex = "(?<=he is wise man)";
System.out.println(str.replaceAll(regex," me too"));
环视匹配的是位置,替换或分隔时不会消除模式p的字符。
String str = "hello";
String regex = "l";
System.out.println(Arrays.toString(str.split(regex)));//结果[he, , o]
String lookAroundRegex = "(?=l)";
System.out.println(Arrays.toString(str.split(lookAroundRegex)));//结果[he, l, lo]
小技巧:匹配不是开头位置 ,[?!^]。
参考资料:
老马说编程
JavaScript正则表达式迷你书
每篇一语
拟把疏狂图一醉,对酒当歌,强乐还无味。——《蝶恋花·伫倚危楼风细细》柳永