一、正则表达式语法
1. 元字符
- 字符匹配符
- 选择匹配符
- 限定符
- 定位符
- 分组组合和反向引用符
元字符(
Metacjaracter
)转义号\\
\\
符号 说明:在我们使用正则表达式去检索某些特殊字符的时候,需要用到转义符号,否则检索不到结果,甚至会报错。特殊:在Java的正则表达式中,两个
\\
代表其他语言中的一个\
需要用到转义符号的字符有
.
、*
、+
、(
、)
、&
、/
、\
、?
、[
、]
、^
、{
、}
案例:
package com.dcxuexi.regexp;import java.util.regex.Matcher;
import java.util.regex.Pattern;/**** @Title RegExp01* @Description TOTD* @Auter DongChuang* @Date 2023/12/30 17:40* @Version 1.0.0*/
public class RegExp01 {public static void main(String[] args) {String content = "a(.dd$.(%^(";//匹配所有字符//String regStr = ".";//匹配点//String regStr = "\\.";//匹配括号String regStr = "\\(";Pattern pattern = Pattern.compile(regStr);Matcher matcher = pattern.matcher(content);while (matcher.find()){System.out.println("找到" + matcher.group(0));}}
}
运行结果:
1.1. 字符匹配符
符号 | 符号含义 | 示例 | 解释 | 结果 |
---|---|---|---|---|
[] | 可接收的字符列表 | [efgh] | e 、f 、g 、h 中的任意1个字符 | e 、h |
[^] | 不接收的字符列表 | [^abc] | 除a 、b 、c 之外的任意1个字符 | d 、h |
- | 连字符 | a-z | 任意单个小写字母 | a 、b |
. | 匹配除\n 以外的任意字符,如果只想匹配. 则需要用\\. | a..b | 以a 开头,b 结尾,中间包括2个任意字符的长度为4的字符串 | addb 、a#*b |
\\d | 匹配单个数字字符,相当于[0-9] | \\d{3}(\\d)? | 包含3个或4个数字的字符串 | 123、7536 |
\\D | 匹配单个非数字字符,相当于[^0-9] | \\D(\\d)* | 以单个非数字字符开头,后接任意个数字字符串 | a 、R75 |
\\w | 匹配单个数字、大小写字母字符、下划线,相当于[0-9、a-z、A-Z、_] | \\d{3}\\w{4} | 以3个数字字符开头的长度为7的数字字母字符串 | 456adFG 、753Tfhd |
\\W | 匹配单个非数字、大小写字母字符,相当于[^0-9、a-z、A-Z、_] | \\W+\\d{2} | 以至少1个非数字字母字符开头,2个数字字符结尾的字符串+代表1个或多个 | #52 、$%^52 |
\\s | 匹配空白字符 | |||
\\S | 匹配非空白字符 |
案例:
package com.dcxuexi.regexp;import java.util.regex.Matcher;
import java.util.regex.Pattern;/**** @Title RegExp02* @Description TOTD* @Auter DongChuang* @Date 2023/12/30 17:45* @Version 1.0.0*/
public class RegExp02 {public static void main(String[] args) {String content = "ad44w8\n5.B6b5d";//小写a-z
// String regStr = "[a-z]";//大写A-Z
// String regStr = "[A-Z]";//[^a-z]{2} 不在a-z之间的连续两个字符
// String regStr = "[^a-z]{2}";//全部都匹配String regStr = ".";//方法一:(?i)不区分大小写
// String regStr = "(?i)wb";Pattern pattern = Pattern.compile(regStr);//方法二:这种方法也可以不区分大小写
// Pattern pattern = Pattern.compile(regStr,Pattern.CASE_INSENSITIVE);Matcher matcher = pattern.matcher(content);while(matcher.find()){System.out.println("找到:" + matcher.group(0));}}
}
[^a-z]{2}
不在a-z
之间的连续两个字符结果:
1.2. 选择匹配符
在匹配某个字符串的时候是选择性的。即:既可以匹配这个,又可以匹配那个,这时需要用到选择匹配符号
|
符号 | 符号含义 | 示例 | 解释 |
---|---|---|---|
` | ` | 匹配` | `之前或之后的表达式 |
案例:
package com.dcxuexi.regexp;import java.util.regex.Matcher;
import java.util.regex.Pattern;/**** @Title RegExp03* @Description TOTD* @Auter DongChuang* @Date 2023/12/30 17:49* @Version 1.0.0*/
public class RegExp03 {public static void main(String[] args) {String content = "盖被子 dd 暖和阿斯顿aa想睡";String regStr = "被子|暖和|睡";Pattern pattern = Pattern.compile(regStr);Matcher matcher = pattern.matcher(content);while(matcher.find()){System.out.println("找到:" + matcher.group(0));}}
}
运行结果:
1.3. 限定符
用于指定其前面的字符和组合项连续出现多少次
符号 | 符号含义 | 示例 | 解释 | 结果 |
---|---|---|---|---|
* | 指定符号重复0次或n 次(无要求) | (abc)* | 仅包含任意abc 的字符串 | abc 、abcabc |
+ | 指定字符重复1次或n 次(至少一次) | m+(abc)* | 以至少1个m 开头,后接任意个abc 的字符串 | m 、mabc 、mabcabc 、mmabc |
? | 指定字符重复0次或1次(最多1次) | m+abc? | 以至少1个m 开头,后接ab 或abc 的字符串,?接最近的 | mab 、mabc 、mmab |
{n} | 只能输入n 个字符 | [abc] | 以abc 中字母组成的任意长度为3的字符串 | abc 、dbc 、adc |
{n,} | 指定至少n 个匹配 | [abcd] | 由abcd 中字母组成的任意长度不小于3的字符串 | abc 、ddddd |
{n,m} | 指定至少n 个但不多于m 个匹配 | [abcd] | 由abcd 中字母组成的任意长度不小于3,并且不大于5的字符串 | abc 、abcd 、aaaaa |
案例:
package com.dcxuexi.regexp;import java.util.regex.Matcher;
import java.util.regex.Pattern;/**** @Title RegExp04* @Description TOTD* @Auter DongChuang* @Date 2023/12/30 17:53* @Version 1.0.0*/
public class RegExp04 {public static void main(String[] args) {String content = "asdmmabffm111111amabmabc";
// String regStr = "m+abc?";//Java默认贪婪匹配,尽可能匹配多的String regStr = "1{4,5}";//结果只有5个1Pattern pattern = Pattern.compile(regStr);Matcher matcher = pattern.matcher(content);while(matcher.find()){System.out.println("找到:" + matcher.group(0));}}
}
运行结果:
1.4. 定位符
规定要匹配的字符串出现的位置,比如在字符串的开始还是在结束的位置
符号 | 符号含义 | 示例 | 解释 | 结果 |
---|---|---|---|---|
^ | 指定起始字符 | ^[0-9]+[a-z]* | 以至少1个数字开头,后接任意个小写字母的字符串 | 2、56f |
$ | 指定结束字符 | ^[0-9]\\-[a-z]+ $ | 以1个数字开头后接连字符- ,并以至少1个小写字母结尾的字符串 | 1-a 、2-dd |
\\b | 匹配目标字符串的边界 | qy\\b | 这里的边界是指离空格最近的和离结束位置最近的 | qydsjdqy qyfffqy |
\\B | 匹配目标字符串的非边界 | qy\\b | 与\\b 相反,指开头第一次出现 | qydsjdqy qyfffqy |
案例:
package com.dcxuexi.regexp;import java.util.regex.Matcher;
import java.util.regex.Pattern;/**** @Title RegExp05* @Description TOTD* @Auter DongChuang* @Date 2023/12/30 17:56* @Version 1.0.0*/
public class RegExp05 {public static void main(String[] args) {String content = "asdmmabffm111111a1";//定位符只能找到一个返回String regStr = "^[a-z]+[0-9]";Pattern pattern = Pattern.compile(regStr);Matcher matcher = pattern.matcher(content);while(matcher.find()){System.out.println("找到:" + matcher.group(0));}}
}
运行结果:
1.5. 分组
1.5.1. 常用分组
常用分组构造形式 | 解释 |
---|---|
(pattern) | 非命名捕获。捕获匹配的子字符串。编号为零的第一个捕获是由整个正则表达式模式匹配的文本,其它捕获结果则根据左括号的顺序从1开始自动编号。 |
(?<name>patter) | 命名捕获。将匹配的子字符串捕获到一个组名称或编号名称中。用于name 的字符串不能包含任何标点符号,并且不能以数字开头。可以使用单引号替代尖括号,例如(?'name') 但是我尝试不能使用 |
非命名分组:
package com.dcxuexi.regexp;import java.util.regex.Matcher;
import java.util.regex.Pattern;/**** @Title RegExp06* @Description TOTD* @Auter DongChuang* @Date 2023/12/30 17:58* @Version 1.0.0*/
public class RegExp06 {public static void main(String[] args) {String content = "dcxuexi dda5524 dadaqy";String regStr = "(\\d\\d)(\\d\\d)";Pattern pattern = Pattern.compile(regStr);Matcher matcher = pattern.matcher(content);while(matcher.find()){System.out.println("找到=" + matcher.group(0));//5524System.out.println("找到=" + matcher.group(1));//55System.out.println("找到=" + matcher.group(2));//24}}
}
运行结果:
命名分组:
package com.dcxuexi.regexp;import java.util.regex.Matcher;
import java.util.regex.Pattern;/**** @Title RegExp06* @Description TOTD* @Auter DongChuang* @Date 2023/12/30 17:58* @Version 1.0.0*/
public class RegExp06 {public static void main(String[] args) {String content = "dcxuexi dda5524 dadaqy";String regStr = "(?<g1>\\d\\d)(?<g2>\\d\\d)";Pattern pattern = Pattern.compile(regStr);Matcher matcher = pattern.matcher(content);while(matcher.find()){System.out.println("找到=" + matcher.group(0));//5524System.out.println("找到=" + matcher.group("g1"));//55System.out.println("找到=" + matcher.group("g2"));//24}}
}
运行结果:
1.5.2. 特别分组
特别分组构造形式 | 解释 |
---|---|
(?:patter) | 匹配pattern 但不捕获该匹配的子表达式,即它是一个非捕获匹配,不存储供以后使用的匹配。这对于用or 字符 |
(?=pattern) | 它是一个非捕获匹配。例如,Windows (?=95 |
(?!pattern) | 该表达式匹配不处于匹配pattern 的字符串的起始点的搜索字符串。它是一个非捕获匹配。例如,Windows (?!95 |
案例:
package com.dcxuexi.regexp;import java.util.regex.Matcher;
import java.util.regex.Pattern;/**** @Title RegExp07* @Description TOTD* @Auter DongChuang* @Date 2023/12/30 18:03* @Version 1.0.0*/
public class RegExp07 {public static void main(String[] args) {//说明非捕获的不能用group(1)去获取String content = "hh阿典yyds 66阿典nb 99阿典xnlxixi";//第一部分(?:patter)
// String regStr = "阿典yyds|阿典nb|阿典xnl";//等价于非捕获分组
// String regStr = "阿典(?:yyds|nb|xnl)";//第二部分(?=pattern)//只找到阿典yyds与阿典nb中的阿典
// String regStr = "阿典(?=yyds|nb)";//第三部分(?!=pattern)//只找到不是阿典yyds与阿典nb中的阿典String regStr = "阿典(?!yyds|nb)";Pattern pattern = Pattern.compile(regStr);Matcher matcher = pattern.matcher(content);while(matcher.find()){System.out.println("找到:" + matcher.group(0));}}
}
运行结果:
2. 非贪婪匹配
Java
正则表达式默认的是贪婪匹配,如果想要进行非贪婪匹配就在需要的地方加一个?
案例:
package com.dcxuexi.regexp;import java.util.regex.Matcher;
import java.util.regex.Pattern;/**** @Title RegExp08* @Description TOTD* @Auter DongChuang* @Date 2023/12/30 18:05* @Version 1.0.0*/
public class RegExp08 {public static void main(String[] args) {String content = "456m";
// String regStr = "\\d+";//贪婪匹配 456String regStr = "\\d+?";//非贪婪匹配 一个一个输出Pattern pattern = Pattern.compile(regStr);Matcher matcher = pattern.matcher(content);while (matcher.find()){System.out.println("找到" + matcher.group(0));}}
}
运行结果:
二、正则表达式三个常用类
java.util.regex
包主要包括以下三个类Pattern
类、Matcher
类和PatternSyntaxException
1. Pattern
类
pattern
对象是一个正则表达式对象。Pattern
类没有公共构造方法。要创建一个Pattern
对象,调用其公共静态方法,它返回一个Pattern
对象。该方法接受一个正则表达式作为它的第一个参数,比如:Patternr=Pattern.compile(pattern);
package com.dcxuexi.regexp;import java.util.regex.Pattern;/**** @Title RegExp10* @Description TOTD* @Auter DongChuang* @Date 2023/12/30 18:09* @Version 1.0.0*/
public class RegExp10 {public static void main(String[] args) {String content = "https://blog.csdn.net/qq_37726813?type=blog";//这里去掉了左右的定位符String regStr = "((http|https)://)([\\w-]+\\.)+[\\w-]+(\\/[\\w-?=&/%.]*)?";System.out.println(Pattern.matches(regStr,content));}
}
运行结果:
2. Matcher
类
Matcher
对象是对输入字符串进行解释和匹配的引擎。与Pattern
类一样,Matcher
也没有公共构造方法。你需要调用Pattern
对象的matcher
方法来获得一个Matcher
对象
package com.dcxuexi.regexp;import java.util.regex.Matcher;
import java.util.regex.Pattern;/**** @Title RegExp11* @Description TOTD* @Auter DongChuang* @Date 2023/12/30 18:13* @Version 1.0.0*/
public class RegExp11 {public static void main(String[] args) {String content = "hello ddd hello dsa$ hello";String regStr = "hello.*";Pattern pattern = Pattern.compile(regStr);Matcher matcher = pattern.matcher(content);while(matcher.find()){System.out.println("================");System.out.println(matcher.start());System.out.println(matcher.end());System.out.println("找到:" + content.substring(matcher.start(),matcher.end()));System.out.println("整体匹配:"+matcher.matches());}//matcher.replaceAll()用于替换regStr = "hello";pattern = Pattern.compile(regStr);matcher = pattern.matcher(content);//原来的文本并不会替换/*** 结果* 你好 ddd 你好 dsa$ 你好* hello ddd hello dsa$ hello*/String newContent = matcher.replaceAll("你好");System.out.println(newContent);System.out.println(content);}
}
运行结果:
3. PatternSyntaxException
PatternSyntaxException
是一个非强制异常类,它表示一个正则表达式模式中的语法错误。
三、分组、捕获、反向引用
1. 分组
我们可以用圆括号组成一个比较复杂的匹配模式,那么一个圆括号的部分我们可以看作是一个子表达式/一个分组。
2. 捕获
把正则表达式中子表达式/分组匹配的内容,保存到内存中以数字编号或显式命名的组里,方便后面引用,从左向右,以分组的左括号为标志,第一个出现的分组的组号为1,第二个为2,以此类推。组0代表的是整个正则式
3. 反向引用
圆括号的内容被捕获后,可以在这个括号后被使用,从而写出一个比较实用的匹配模式,这个我们称为反向引用,这种引用既可以是在正则表达式内部,也可以是在正则表达式外部,内部反向引用
\\分组号
,外部反向引用$分组号
package com.dcxuexi.regexp;import java.util.regex.Matcher;
import java.util.regex.Pattern;/**** @Title RegExp12* @Description TOTD* @Auter DongChuang* @Date 2023/12/30 18:19* @Version 1.0.0*/
public class RegExp12 {public static void main(String[] args) {String content = "hel522511lo d1234111dd h55ell3o dsa$ hello";//匹配连续相同的两个数字//String regStr = "(\\d)\\1";//String regStr = "(\\d)\\1{1}";//String regStr = "(\\d)(\\d)";//匹配连续三个相同的字符//String regStr = "(\\d)\\1{2}";//匹配个位与千位相同十位与百位相同的字符 如:1221String regStr = "(\\d)(\\d)\\2\\1";Pattern pattern = Pattern.compile(regStr);Matcher matcher = pattern.matcher(content);while(matcher.find()){System.out.println("找到:"+matcher.group(0));}}
}
运行结果:
反向引用就是通过分组的组名去再次利用分组
四、正则表达式应用
1. 简单应用
package com.dcxuexi.regexp;import java.util.regex.Matcher;
import java.util.regex.Pattern;/**** @Title RegExp13* @Description TOTD* @Auter DongChuang* @Date 2023/12/30 18:22* @Version 1.0.0*/
public class RegExp13 {public static void main(String[] args) {//QQ号 1-9开头的String content = "10006";String regStr = "^[1-9][0-9]{4,10}$";Pattern pattern = Pattern.compile(regStr);Matcher matcher = pattern.matcher(content);if(matcher.find()){System.out.println("满足格式");}else{System.out.println("不满足格式");}}
}
运行结果:
2. 判断url
package com.dcxuexi.regexp;import java.util.regex.Matcher;
import java.util.regex.Pattern;/**** @Title RegExp14* @Description TOTD* @Auter DongChuang* @Date 2023/12/30 18:25* @Version 1.0.0*/
public class RegExp14 {public static void main(String[] args) {String content = "https://blog.csdn.net/qq_37726813?type=blog";/**** 1.先用((http|https)://)确定url的开始部分 http:// | https://* 2.再用([\w-]+\.)+[\w-]+确定blog.csdn.net* 3.再用[\w-]+(\/[\w-?=&/%.]*)?确定/qq_37726813?type=blog* 说明[]里面的东西都会是原样的*/String regStr = "^((http|https)://)([\\w-]+\\.)+[\\w-]+(\\/[\\w-?=&/%.]*)?$";Pattern pattern = Pattern.compile(regStr);Matcher matcher = pattern.matcher(content);if(matcher.find()){System.out.println("满足格式");}else{System.out.println("不满足格式");}}
}
运行结果:
3. 结巴去重
package com.dcxuexi.regexp;import java.util.regex.Matcher;
import java.util.regex.Pattern;/**** @Title RegExp15* @Description TOTD* @Auter DongChuang* @Date 2023/12/30 18:28* @Version 1.0.0*/
public class RegExp15 {public static void main(String[] args) {String content = "我....我要....学学学学...编程java!";//1.去掉所有.(我我要学学学学编程java!)Pattern pattern = Pattern.compile("\\.");Matcher matcher = pattern.matcher(content);content = matcher.replaceAll("");System.out.println(content);//2.去掉重复的字//使用反向引用$1来替换匹配到的内容content = Pattern.compile("(.)\\1+").matcher(content).replaceAll("$1");System.out.println(content);}
}
运行结果:
说明:
为什么一个
$1
就可以替换不同的字符呢,为什么不只是替换第一个字符,可能你有这样的疑问,解释:这还要看源码,通过源码我们可以得知分组的位置不是递增的而是一次一次被替换的,因此他始终会在1分组,开始
我
在1分组中,然后用我
替换了全部连续的我
,之后要替换我
进入第一个分组,重复上述操作等等
4. 分割字符串
package com.dcxuexi.regexp;/**** @Title RegExp16* @Description TOTD* @Auter DongChuang* @Date 2023/12/30 18:30* @Version 1.0.0*/
public class RegExp16 {public static void main(String[] args) {String content = "dc#dha-jd=你好=aj?d51~ddd";String[] split = content.split("#|-|~|\\d+|=|\\?");for (String i :split){System.out.println(i);}}
}
运行结果: