1. 正则表达式介绍
最早正则表达式的产生是用于Unix的编辑器工具及grep工具,所以针对于正则表示规范可以通过The Single UNIX® Specification进行查询<Regular Expressions>
2. Java使用正则表达式
2.1 Pattern类和Matcher类:
java.util.regex包中提供了Pattern和Matcher两个类,用来实现通过正则表达式实现的字符串匹配、搜索、分割、替换等操作!
代码示例:
public class ReTest {
public static void main(String[] args) {
/*构造一个符合"\d{3}"含义的Pattern实例*/
Pattern pattern = Pattern.compile("(\\d{3})(%)");
/*通过Pattern类的split方法,进行字符串分隔*/
String[] sa = pattern.split("a123%b456%c789%");
Arrays.stream(sa).forEach(x -> System.out.println(x));
/*通过Pattern类的matcher方法,构造一个需要与pattern正则表达式匹配的Matcher实例*/
Matcher matcher= pattern.matcher("123%");
/*通过Matcher类的matches方法,进行正则表达式匹配,符合返回true,不符合返回false*/
System.out.println(matcher.matches());
/*通过Matcher类的replaceAll方法,进行依照正则表达式的字符串替换*/
Matcher matcher1 = pattern.matcher("hello 123%");
System.out.println(matcher1.replaceAll("abc"));
/*通过Matcher类的replaceAll方法,进行依照正则表达式的字符串替换*/
Matcher matcher2 = pattern.matcher("hello 123%");
System.out.println(matcher2.replaceFirst("abc"));
/*通过Matcher类的group方法,进行依照正则表达式的字符串分组,0表示整个字符串,具体分组从1开始*/
Matcher matcher3 = pattern.matcher("123%");
matcher3.matches();
System.out.println("group0 " + matcher3.group(0));
System.out.println("group1 " + matcher3.group(1));
System.out.println("group2 " + matcher3.group(2));
/*通过Matcher类的find方法,进行依照正则表达式的字符串差找*/
String s = "hello 123%";
Matcher matcher4 = pattern.matcher(s);
while(matcher4.find()) {
String res = s.substring(matcher4.start(),matcher4.end());
System.out.println(res);
}
}
}
输出:
Ps:
- 调用Matcher类中的group方法,需要注意分组下标是从1开始的,group(0)表示字符串本身
- Matcher类中的replaceAll和replaceFirst方法是通过find方法实现,但需要注意的是find方法的实现与matches方法实现不同;所以导致有些场景下,它俩并不等价;即matches匹配返回true的字符串并不一定是find的结果,例如下面的非贪心匹配;同时find方法可以找到字符串也不一定可以通过matches方法匹配返回true,例如下面的(?=表达式)等格式正则表达式!
- 正则表达式后向引用,在调用replaceAll和replaceFrist方式时,可以使用$1、$2等参数来表示正则表达式的第一分组字符串和第二分组字符串
代码示例:
public class ReTest {
public static void main(String[] args) {
String s = "123%456a786&";
Pattern pattern = Pattern.compile("(\\d{3})(.)");
Matcher matcher = pattern.matcher(s);
/*$2表示%,$1表示123;$2表示a,$1表示456;$2表示&,$1表示786*/
System.out.println(matcher.replaceAll("$2$1"));
}
}
输出:
2.2 String类中正则表达式的使用:
- String类中的split方法、replaceAll和replaceFirst方法、matches方法都是依赖Pattern类和Matcher类实现的!
- 在使用上述方法时,一定要特别注意正则表达式特殊字符的转义,否则可能会造成使用错误;比如说"abc|123|def".replaceAll("|","@"),则不能成功将字符串中的'|'字符替换成'@',因为'|'字符需要转义为'\\|'字符;但是String类中的replace()方法则没有问题,因为他是文本匹配而不是通过正则表达式匹配!
2.3 Java正则表达式的转义字符:
- 由于Java中正则表达式也是一个字符串,同时Java本身在字符串描述的时候就需要使用'\'字符对部分特殊字符进行转义;所以Java中'\n'字符要被表示为正则表达式的时候需要表示为'\\n';第一个'\'用来说明在字符串转义的时候,对第二个'\'进行转义,第二个'\'用来在完成字符串转义后,用来对后面的n进行正则表达式的转义!所以如果在正则表达式中需要表示一个'\'字符,需要为'\\\\'!
- 同样正则表达式中的特殊字符,如果需要仅仅表示字符本身含义而没有语法含义的时候,都需要加上转义字符'\';例如希望匹配?字符、*字符、.字符、-字符、|字符等等,字符的本意的时候,都需要加上转义字符"\\?",“\\*”等!
3. 正则表达式语法:
3.1 单字符匹配:
.字符:用来匹配除\n\t以为的一个任意字符
"a.b表示a+任意除\n\t意外的一个字符+b
\w字符:用来匹配任意一个字母、数字、下划线字符
"a\wb表示a+任意一个字母、数字、下划线+b
\d字符:用来匹配任意一个数字字符
"a\db表示a+任意一个字母、数字、下划线+b
\s字符:用来匹配\f\n\r\t\v中的任意一个字符
"a\sb"表示a+\f\n\r\t\v字符中的任意一个字符+b
3.2 重复匹配:
*字符:匹配零个或者多个*字符前面字符
"a*b*"表示n个a+n个b组成的字符串,n >= 0
+字符:匹配一个或者多个+字符前面字符
"a+b+"表示n个a+n个b组成的字符串,n > 0
?字符:匹配零个或者一个?字符前面字符
"a?b?"表示n个a+n个b组成的字符串,1 >= n >= 0
{}符号:匹配{}里描述数量的{}符号前面的字符
"a{3,5}b{4}c{1,}"表示n个a+m个b+k个c组成的字符串,5 >= n >= 3,m = 4,k >= 1
3.3 限定匹配:
[]符号:通过[]符号里面的范围匹配一个字符,可以枚举表示[012345]、范围表示[0-5];反向表示[^012345]、[^0-5]
"[012]"表示一个字符的范围为0、1、2;"[0-2]"为另一种表示形式
"[^012]"表示一个字符的范围不为0、1、2;"[^0-2]"为另一种表示形式
|字符:选择匹配一个通过|字符分割的字符串
"(AB|BA|ABA)"表示匹配"AB"、"BA"、"ABA"
3.4 占位匹配:
占位匹配的符号并不会真正参与字符的匹配,仅仅提供的是占位说明,主要用于正则表达式的搜索替换场景
\b字符:用来指示当前位置是一个单词边界位置,即字符串与空格之间的位置
"hello\s\ba\b"表示匹配"hello a",可见\b并不充当字符的匹配,仅仅起到一个位置说明的作用,用来说明当前位置为单词边界
^字符:用来指示一个字符串的开头
"^hello"表示匹配"hello"字符串并且"hello"在字符串开始位置,注意如果字符串为"hello1"则不能匹配
$字符:用来指示一个字符串的结尾
"hello$"表示匹配"hello"字符串并且"hello"在字符串结束位置,注意如果字符串为"1hello"则不能匹配
代码示例:
public class ReTest {
public static void main(String[] args) {
String s = "helloa";
System.out.println(s.replaceAll("^hello","java"));
String s1 = "ahello";
System.out.println(s1.replaceAll("hello$","java"));
}
}
输出:
3.5 反义匹配:
\W字符:匹配\w字符补集中的任意一个字符,即\w的反义匹配
\D字符:匹配\d字符补集中的任意一个字符,即\d的反义匹配
\S字符:匹配\s字符补集中的任意一个字符,即\s的反义匹配
\B字符:与\b同为占位匹配,指示当前位置不是一个单词边界位置,即\b的反义匹配
3.6 非贪心匹配:
*字符、?字符、+字符、{a,b}都存在贪心匹配的含义,即都匹配为一个范围;如果使他们都使用最小的匹配范围,就是非贪心匹配的概念!正则表达式中通过在上述符号后面增加?字符实现!
但是需要注意的是,非贪心匹配的实现仅仅对正则表达式分组、搜索、替换操作有效!对字符串正则表达式规则匹配,即matches方法无效!
代码示例:
public class ReTest {
public static void main(String[] args) {
/*非贪心匹配在正则表达式分组中的使用*/
String s = "100000";
Pattern pattern = Pattern.compile("(\\d{1,3}?)(0*)");
Matcher matcher = pattern.matcher(s);
System.out.println(matcher.matches());
/*如果没有?符号,group(1)则输出100,group(2)则输出000*/
/*根据范围限定第一组应该匹配最小范围1,所以输出1*/
System.out.println(matcher.group(1));
/*第二组,则应该输出00000*/
System.out.println(matcher.group(2));
/*非贪心匹配在正则表达式字符串替换中的使用*/
String s1 = "100000";
Pattern pattern1 = Pattern.compile("\\d{1,3}?");
Matcher matcher1 = pattern1.matcher(s1);
/*如果没有?符号,输出结果为"a000"*/
/*当使用非贪心匹配规则后,输出结果为"a00000",说明匹配{1,3}的最小范围1*/
System.out.println(matcher1.replaceFirst("a"));
/*非贪心匹配在正则表达式字符串替换中的使用*/
String s2 = "100";
//String s2 = "10";
//String s2 = "1";
Pattern pattern2 = Pattern.compile("\\d{1,3}?");
Matcher matcher2 = pattern2.matcher(s2);
/*find方法并不一定与matches方法等价;在使用find方法进行字符串查找的时候,非贪心匹配的时候,只能找到最小的匹配规则
* 但是在matches方法中,即使使用了?符号标记了非贪心匹配,依然可以匹配"1"、"10"、"100"等字符串,并没有严格进行非贪心匹配*/
System.out.println(matcher2.matches());
}
}
输出:
3.7 ?字符相关的几类表达式:
- (?:表达式):正则表达式在通过()分组的时候,可以使用?符号,来标识当前分组不记录到group当中,此符号仅影响分组记录并不影响匹配规则!例如:(?:"ab")("cd")("ef")表达式,匹配规则依然是匹配"abcdef",但group(1)为"cd",group(2)为"ef",没有group(3)
- XXX(?=表达式AAA):匹配后缀为表达式AAA的前缀字符串XXX,主要应用在字符串的查找和替换!虽然可以替换或者查找符合条件的XXX字符串,但是由于条件中必须要有AAA,所以不存在一个字符串可以满足该正则表达式使得matches方法返回true!
- XXX(?!表达式AAA):为XXX(?=表达式AAA)的反匹配,匹配后缀不为表达式AAA的前缀字符串XXX
- (?<=表达式AAA)XXX:匹配前缀为表达式AAA的后缀字符串XXX,原理与XXX(?=表达式AAA)一致,仅匹配顺序相反!
- (?<!表达式AAA)XXX:为(?<=表达式AAA)XXX的反匹配,匹配前缀不为表达式AAA的后缀字符串XXX
代码示例:
public class ReTest {
public static void main(String[] args) {
/*示例:XXX(?=表达式AAA)*/
String s1 = "Hello123";
Pattern pattern1 = Pattern.compile("Hello(?=\\d{3})");
Matcher matcher1 = pattern1.matcher(s1);
System.out.println(matcher1.replaceAll("ok"));
/*示例:XXX(?!表达式AAA)*/
String s2 = "Hello12";
Pattern pattern2 = Pattern.compile("Hello(?!\\d{3})");
Matcher matcher2 = pattern2.matcher(s2);
System.out.println(matcher2.replaceAll("ok"));
/*示例:(?<=表达式AAA)XXX*/
String s3 = "123Hello";
Pattern pattern3 = Pattern.compile("(?<=\\d{3})Hello");
Matcher matcher3 = pattern3.matcher(s3);
System.out.println(matcher3.replaceAll("ok"));
/*示例:(?<!表达式AAA)XXX*/
String s4 = "12Hello";
Pattern pattern4 = Pattern.compile("(?<!\\d{3})Hello");
Matcher matcher4 = pattern4.matcher(s4);
System.out.println(matcher4.replaceAll("ok"));
/*不存在一个字符串满足上述正则表达式的matches匹配*/
String s = "Hello";
Pattern pattern = Pattern.compile("Hello(?=\\d{3})");
Matcher matcher = pattern.matcher(s);
System.out.println(matcher.matches());
}
}
输出:
3.8 转义字符相关的匹配:
- \f \r \n \t \v等转义字符:可以表示匹配一个非打印字符
- \八进制:\字符后面+0+数字满足八进制要求即可表示匹配一个八进制的转义字符
- \十六进制:\字符后面+x+满足十六进制要求的字符即可表示匹配一个十六进制的转义字符
- \unicode码:\字符后面+u+满足unicode编码要求的字符既可以表示匹配一个unicode码的转义字符
- \数字:\字符后面+数字(num > 0),表示针对前面通过分组匹配到的子串进行下标获取!看示例中详细说明!
代码示例:
public class ReTest {
public static void main(String[] args) {
String s = "ac11ac";
/*该正则表达式中存在两个分组
* 分组1为[a-d]{2},匹配字符为"ac"
* 分组2为[1-3],匹配字符为"1"
* 所以\1为"ac"子串的替换
* \2为"1"子串的替换*/
Pattern pattern = Pattern.compile("([a-d]{2})([1-3])\\2\\1");
Matcher matcher = pattern.matcher(s);
System.out.println(matcher.matches());
}
}
3.9 运算符优先级:
运算符 | 描述 |
---|---|
\ | 转义符 |
()、 (?:)、 (?=)、 [] | 圆括号和方括号 |
*、+,、?、{n}、{n,}、{n,m} | 限定符 |
^、$、 \任意元字符、任意字符 | 位置描述及占位 |
| | 或操作 |