正则表达式通常被用来检索、替换那些符合某个模式(规则)的文本。现阶段很多语言有正则的实现,比如java,python等等。简单的关于字符串的替换查找等的操作可以通过字符串的API来操作,但是有些情况,通过API要进行很多额外的操作才能完成的功能,通过正则一个简单的表达式就可以轻松搞定,不信请往下看。
基础语法
一. 表示位置
1. ^
匹配输入字符串的开始位置。$
匹配输入字符串的结束位置
@Test
public void test15() {
Pattern pattern = Pattern.compile("^a.*b$");
Matcher matcher1 = pattern.matcher("adb");
System.out.println(matcher1.find()); //true
Matcher matcher2 = pattern.matcher("db");
System.out.println(matcher2.find()); //true
Matcher matcher3 = pattern.matcher("ad");
System.out.println(matcher3.find()); //true
}
}
二. 表示次数
3. *
匹配前面的子表达式零次或多次,相当于{0,}
@Test
public void test16(){
Pattern pattern = Pattern.compile("a*");
Matcher matcher1 = pattern.matcher("");
System.out.println(matcher1.find()); //true
Matcher matcher2 = pattern.matcher("a");
System.out.println(matcher2.find()); //true
Matcher matcher3 = pattern.matcher("aa");
System.out.println(matcher3.find()); //true
}
4. +
匹配前面的子表达式一次或多次,相当于{1,}
@Test
public void test17(){
Pattern pattern = Pattern.compile("a+");
Matcher matcher1 = pattern.matcher("");
System.out.println(matcher1.find()); //false
Matcher matcher2 = pattern.matcher("a");
System.out.println(matcher2.find()); //true
Matcher matcher3 = pattern.matcher("aa");
System.out.println(matcher3.find()); //true
}
5. ?
匹配前面的子表达式零次或一次,相当于{0,1}
@Test
public void test18(){
Pattern pattern = Pattern.compile("do(es)?");
Matcher matcher1 = pattern.matcher("do");
System.out.println(matcher1.find()); //true
Matcher matcher2 = pattern.matcher("does");
System.out.println(matcher2.find()); //true
Matcher matcher3 = pattern.matcher("doe");
System.out.println(matcher3.find()); //true
}
0次或者1次,那么也就是do或者does,所以包含do或者does的都会被选出来,doe也包含do,所以也查找到了。
6. {n}
n是一个非负整数。匹配确定的n次
@Test
public void test19(){
Pattern pattern = Pattern.compile("n{2}");
Matcher matcher1 = pattern.matcher("abnn");
System.out.println(matcher1.find()); //true
Matcher matcher2 = pattern.matcher("abn");
System.out.println(matcher2.find()); //false
Matcher matcher3 = pattern.matcher("ab");
System.out.println(matcher3.find()); //false
}
7. {n,}
n是一个非负整数。至少匹配n次
@Test
public void test20(){
Pattern pattern = Pattern.compile("n{2,}");
Matcher matcher1 = pattern.matcher("abnnn");
System.out.println(matcher1.find()); //true
Matcher matcher2 = pattern.matcher("abnn");
System.out.println(matcher2.find()); //true
Matcher matcher3 = pattern.matcher("abn");
System.out.println(matcher3.find()); //false
}
8. {n,m}
m和n均为非负整数,其中n<=m。最少匹配n次且最多匹配m次
@Test
public void test21() {
System.out.println("abnnnn".replaceAll("n{2,3}", "")); //abn 替换了前面3个n为空的字符串
System.out.println("abnnn".replaceAll("n{2,3}", "")); //ab 替换了3个n为空的字符串
System.out.println("abnn".replaceAll("n{2,3}", "")); //ab 替换了2个n为空的字符串
System.out.println("abn".replaceAll("n{2,3}", "")); //abn 未匹配到,不做替换,还是原来字符创
}
9. 特殊的?
:当该字符紧跟在任何一个其他限制符 (*, +, ?, {n}, {n,}, {n,m}) 后面时,匹配模式是非贪婪的。非贪婪模式尽可能少的匹配所搜索的字符串,而默认的贪婪模式则尽可能多的匹配所搜索的字符串
@Test
public void test22() {
Pattern pattern = Pattern.compile("n{2,3}?"); //贪婪模式
Matcher matcher1 = pattern.matcher("abnnnnn");
while (matcher1.find()){
System.out.println(matcher1.group());
}
}
结果:nn输出两遍。因为是懒惰模式,所以会找最小满足要求的,也就是连续的两个n,因为我们现在有连续5个n,所以可以找到2次
三.表示功能(实在找不到别的词了)
10. ·
匹配除换行符(\n)与回车符(\r)之外的任何单个字符。
@Test
public void test23(){
Pattern pattern = Pattern.compile(".");
Matcher matcher1 = pattern.matcher("a");
System.out.println(matcher1.find()); //true
Matcher matcher2 = pattern.matcher("3");
System.out.println(matcher2.find()); //true
Matcher matcher3 = pattern.matcher("\t");
System.out.println(matcher3.find()); //true
Matcher matcher4 = pattern.matcher("\r");
System.out.println(matcher4.find()); //false
Matcher matcher5 = pattern.matcher("\n");
System.out.println(matcher5.find()); //false
}
11. \d
匹配一个数字字符,等价于 [0-9]。\D
匹配一个非数字字符。等价于 [^0-9]
@Test
public void test24(){
Pattern pattern = Pattern.compile("^\\d+$");
Matcher matcher1 = pattern.matcher("23");
System.out.println(matcher1.find()); //true
Matcher matcher2 = pattern.matcher("3d");
System.out.println(matcher2.find()); //false
Pattern pattern2 = Pattern.compile("^\\D+$");
Matcher matcher3 = pattern2.matcher("23");
System.out.println(matcher3.find()); //false
Matcher matcher4 = pattern2.matcher("3d");
System.out.println(matcher4.find()); //false
Matcher matcher5 = pattern2.matcher("dd");
System.out.println(matcher5.find()); //true
}
12. \w
匹配字母、数字、下划线,等价于'[A-Za-z0-9_]'。\W
匹配非字母、数字、下划线,等价于 '[^A-Za-z0-9_]'。
@Test
public void test25(){
Pattern pattern = Pattern.compile("^\\w+$");
Matcher matcher1 = pattern.matcher("3Dd_");
System.out.println(matcher1.find()); //true
Pattern pattern2 = Pattern.compile("^\\W+$");
Matcher matcher3 = pattern2.matcher("\t\n\r\b");
System.out.println(matcher3.find()); //true
}
13. \s
匹配任何空白字符,包括空格、制表符、换页符等等。\S
匹配任何非空白字符。
@Test
public void test26(){
Pattern pattern = Pattern.compile("^\\s+$");
Matcher matcher1 = pattern.matcher(" \t\r\n");
System.out.println(matcher1.find()); //true
Pattern pattern2 = Pattern.compile("^\\S+$");
Matcher matcher3 = pattern2.matcher("aA3_");
System.out.println(matcher3.find()); //true
}
四. 范围中匹配一个
14. x|y
匹配x或y。
@Test
public void test27(){
Pattern pattern = Pattern.compile("a|b");
Matcher matcher1 = pattern.matcher("ac");
System.out.println(matcher1.find()); //true
Matcher matcher2 = pattern.matcher("bc");
System.out.println(matcher2.find()); //true
Matcher matcher3 = pattern.matcher("cc");
System.out.println(matcher3.find()); //false
}
15. [xyz]
匹配所包含的任意一个字符。[^xyz]
匹配未包含的任意字符。
@Test
public void test29(){
Pattern pattern = Pattern.compile("^[abc]+$");
Matcher matcher1 = pattern.matcher("aA");
System.out.println(matcher1.find()); //false
Matcher matcher2 = pattern.matcher("bb");
System.out.println(matcher2.find()); //true
Matcher matcher3 = pattern.matcher("CC");
System.out.println(matcher3.find()); //false
Pattern pattern2 = Pattern.compile("^[^abc]+$");
Matcher matcher4 = pattern2.matcher("aA");
System.out.println(matcher4.find()); //false
Matcher matcher5 = pattern2.matcher("bb");
System.out.println(matcher5.find()); //false
Matcher matcher6 = pattern2.matcher("CC");
System.out.println(matcher6.find()); //true
}
16. [a-z]
匹配指定范围内的任意字符。[^a-z]
匹配任何不在指定范围内的任意字符。
@Test
public void test28(){
Pattern pattern = Pattern.compile("^[a-z]+$");
Matcher matcher1 = pattern.matcher("aA");
System.out.println(matcher1.find()); //false
Matcher matcher2 = pattern.matcher("bb");
System.out.println(matcher2.find()); //true
Matcher matcher3 = pattern.matcher("CC");
System.out.println(matcher3.find()); //false
Pattern pattern2 = Pattern.compile("^[^a-z]+$");
Matcher matcher4 = pattern2.matcher("aA");
System.out.println(matcher4.find()); //false
Matcher matcher5 = pattern2.matcher("bb");
System.out.println(matcher5.find()); //false
Matcher matcher6 = pattern2.matcher("CC");
System.out.println(matcher6.find()); //true
}
五. 捕获组
捕获组就是把正则表达式中子表达式匹配的内容,保存到内存中以数字编号或显式命名的组里,方便后面引用。捕获组有两种形式,一种是普通捕获组,另一种是命名捕获组,通常所说的捕获组指的是普通捕获组
17. 普通捕获组
如果没有显式为捕获组命名,即没有使用命名捕获组,那么需要按数字顺序来访问所有捕获组。在只有普通捕获组的情况下,捕获组的编号是按照“(”出现的顺序,从左到右,从1开始进行编号的 。编号为0的捕获组,指的是正则表达式整体,这一规则在支持捕获组的语言中,基本上都是适用的
@Test
public void test12() {
String pat = "\\S*(\\d{4})-(\\d{2}-(\\d{2}))\\S*";
Pattern pattern = Pattern.compile(pat);
Matcher matcher = pattern.matcher("今天是2018-12-13,我好开心");
System.out.println(matcher.find()); //true
System.out.println(matcher.group(0)); //今天是2018-12-13,我好开心
System.out.println(matcher.group(1)); //2018
System.out.println(matcher.group(2)); //12-13
System.out.println(matcher.group(3)); //13
}
18. 命名捕获组
命名捕获组通过显式命名,可以通过组名方便的访问到指定的组,而不需要去一个个的数编号,同时避免了在正则表达式扩展过程中,捕获组的增加或减少对引用结果导致的不可控。不过容易忽略的是,命名捕获组也参与了编号的,在只有命名捕获组的情况下,捕获组的编号也是按照“(”出现的顺序,从左到右,从1开始进行编号的 。
@Test
public void test13() {
String pat = "\\S*(?<year>\\d{4})-(?<date>\\d{2}-(?<day>\\d{2}))\\S*";
Pattern pattern = Pattern.compile(pat);
Matcher matcher = pattern.matcher("今天是2018-12-13,我好开心");
System.out.println(matcher.find()); //true
System.out.println(matcher.group(0)); //今天是2018-12-13,我好开心
System.out.println(matcher.group(1)); //2018
System.out.println(matcher.group("year")); //2018
System.out.println(matcher.group(2)); //12-13
System.out.println(matcher.group("date")); 12-13
System.out.println(matcher.group(3)); //13
System.out.println(matcher.group("day")); //13
}
上面所说的是是获取匹配,下面我们说费获取匹配。正向表示匹配前面,反向表示匹配后面。又叫零宽断言
19. (?:pattern)
匹配 pattern 但不获取匹配结果,也就是说这是一个非获取匹配,不进行存储供以后使用。这在使用 "或" 字符 (|) 来组合一个模式的各个部分是很有用
@Test
public void test10() {
String str = "industry|industries";
String str1 = str.replaceAll("(industr(?:y|ies))", "");
System.out.println(str1); // | 说明都匹配到了
String str2 = str.replaceAll("(industr[y|ies])", "");
System.out.println(str2); // |es 说明都匹配到了
}
20. (?=pattern)
正向肯定预查,在任何匹配pattern的字符串开始
处匹配查找字符串。匹配pattern前面的位置
@Test
public void test1() {
String str = "windows95,windows100";
//正向肯定预查,匹配windows95中的windows,或者windows1000中的windows
str = str.replaceAll("(windows(?=95|1000))", "");
System.out.println(str); //95,windows100
}
21. (?!pattern)
正向否定预查,在任何不匹配pattern的字符串开始
处匹配查找字符串。匹配pattern后面的位置
@Test
public void test2() {
String str = "swindows95,rwindows100";
//正向否定预查,不匹配windows95中的windows,或者不匹配windows1000中的windows
str = str.replaceAll("(windows(?!95|1000))", "");
System.out.println(str); //swindows95,r100
}
22. (?<=pattern)
反向肯定预查,与正向肯定预查类似,只是方向相反。匹配后面跟的不是pattern的位置
@Test
public void test3() {
String str = "95windows,100windows";
//反向肯定预查,匹配95windows中的windows或者是1000windows中的windows
str = str.replaceAll("(?<=95|1000)windows", "");
System.out.println(str); //95,100windows
}
23. (?<!pattern)
反向否定预查,与正向否定预查类似,只是方向相反。匹配前面不是pattern的位置
@Test
public void test4() {
String str = "95windows,100windows";
//反向否定预查,不匹配95windows中的windows和1000windows中的windows
str = str.replaceAll("(?<!95|1000)windows", "");
System.out.println(str); //95windows,100
}
实战
1.压缩xml
我们发送的或者接收到的xml是有格式的。
<?xml version="1.0" encoding="GBK" standalone="yes"?>
<user>
<header>
<name>linyang</name>
<age>26</age>
</header>
<body>
<address>地址</address>
<postcode>234321</postcode>
</body>
</user>
我们需要将标签之间的空白字符都去掉,完成我们的压缩目的,一句代码搞定。
@Test
public void test40(){
String str="<?xml version=\"1.0\" encoding=\"GBK\" standalone=\"yes\"?>\n" +
"\<user>\n" +
" \<header>\n" +
" \<name>shuqi\</name>\n" +
" \<age>26\</age>\n" +
" \</header>\n" +
" \<body>\n" +
" \<address>地址\</address>\n" +
" \<postcode>234321\</postcode>\n" +
" \</body>\n" +
"\</user>";
str = str.replaceAll("(?<=>)\\s+(?=<)", "");
System.out.println(str);
}
结果是:
<?xml version="1.0" encoding="GBK" standalone="yes"?><user><header><name>shuqi</name><age>26</age></header><body><address>地址</address><postcode>234321</postcode></body></user>
很好的完成了压缩的目的,利用的就是零宽断言。
本文收录的不是全部的正则表达式,只是相对来说比较常用的,他再文本匹配、搜索、替换都有较高的应用,掌握它我们会事半功倍。好玩的示例补充中,未完待续,敬请期待。