正则表达式
正则表达式语法
转义字符
- . * + ( ) $ / \ ? [ ] ^ { } - | ? 等
- 上述元字符在正则表达式中使用需要用到\\
字符匹配符
符号 | 含义 | 示例 | 解释 |
---|---|---|---|
[ ] | 可接收的字符列表 | [efgh] | e、f、g、h中的任意1个字符 |
[^] | 不接收的字符列表 | [^abc] | 除a、b、c之外的任意1个字符,包含数字和特殊符号 |
- | 连字符 | A-Z | 任意单个大写字母 |
. | 匹配\n以外的任何字符 | a…b | 以a开头,b结尾,中间包括2个任意字符的长度为4的字符串 |
\\d | 匹配单个数字字符,相当于[0-9] | \\d{3}(\\d)? | 包含3个或4个数字的字符串 |
\\D | 匹配单个非数字字符,相当于 [^0-9] | \\D(\\d)* | 以单个非数字字符开头,后接任意个数字字符串 |
\\w | 匹配单个数字、大小写字母字符、下划线,相当于 [0-9a-zA-Z_] | \\d{3}\\w{4} | 以三个数字字符开头的长度为7的数字字母下划线字符串 |
\\W | 匹配单个非数字、大小写字母字符、下划线,相当于 [^0-9a-zA-Z_] | \\W+\\d{2} | 以至少一个非数字字母下划线字符开头,2个数字字符串结尾的字符串 |
\\s | 匹配任何空白字符(空格,制表符等) | \\s\\d | 以一个空白字符开头,和数字字符结尾长度为2字符串 |
\\S | 匹配任何非空白字符(空格,制表符等) | \\S\\d | 以一个非空白字符开头,数字字符结尾长度为2字符串 |
选择匹配符
符号 | 含义 | 示例 | 解释 |
---|---|---|---|
| | 匹配"|"之前或之后的表达式 | ab|cd | ab或者cd |
限定符
符号 | 含义 | 示例 | 解释 |
---|---|---|---|
* | 指定字符重复0次或n次 | (abc)* | 仅包含任意个abc的字符串 |
+ | 指定字符重复1次或n次 | m+(abc)* | 至少以一个m开头,后接任意个abc的字符串 |
? | 指定字符重复0次或1次 | m+abc? | 至少1个m开头,后接ab或abc的字符串 |
{n} | 只能输入n个字符 | [abcd]{3} | 由abcd中任意字母组成长度为3的字符串 |
{n,} | 指定至少n个匹配 | [abcd]{3,} | 由abcd中任意字母组成长度不小于3的字符串 |
{n,m} | 指定至少n个但不多于m个匹配 | [abcd]{3,5} | 由abcd中任意字母组成长度不小于3,不大于5的字符串 |
- 注意
- Java匹配时贪婪匹配,尽可能匹配多的字符,比如"aaaaa", {3,},则将会匹配"aaaaa"
定位符
符号 | 含义 | 示例 | 解释 |
---|---|---|---|
^ | 指定起始字符 | ^[0-9]+[a-z]* | 至少以1个数字开头,后接任意个小写字母的字符串 |
$ | 指定结束字符 | ^[0-9]\\-[a-z]+$ | 以1个数字开头后接连字符"-",并以至少1个小写字母结尾的字符串 |
\\b | 匹配目标字符串的边界 | han\\b | 这里说的字符串的边界指的是子串间有空格,或者是目标字符串的结束位置 |
\\B | 匹配目标字符串的非边界 | han\\B | 和\\b相反 |
分组和捕获
- 分组
- 我们可以用圆括号组成一个比较复杂的匹配摸式,那么一个圆括号的部分我们可以看作是一个子表达式/一个分组。
- 捕获
- 把正则表达式中子表达式/分组匹配的内容,保存到内存中以数字编号或显式命名的组里,方便后面引用,从左向右,以分组的左括号为标志,第一个出现的分组的组号为1,第二个为2,以此类推。组0代表的是整个正则式。
- 将匹配的字符串捕获到一个组名称或编号名称中。用name的字符串不能包含任何标点符号,并且不能以数字开头。可以使用单引号代替尖括号,例如(?‘name’)
常用分组构造形式 | 说明 |
---|---|
(pattern) | 非命名捕获。捕获匹配的子字符串。编号为零的第一个捕获是由整个正则表达式模式匹配的文本,其他捕获结果则根据左括号的顺序从1开始自动编号。 |
(?patter) | 命名捕获。将匹配的字符串捕获到一个组名称或编号名称中。用name的字符串不能包含任何标点符号,并且不能以数字开头。可以使用单引号代替尖括号,例如(?‘name’) |
(?:pattern) | 匹配pattern但不捕获该匹配的子表达式,即它是一个非捕获匹配,不存储供以后使用的匹配。这对于"or"字符(|)组合模式部件的情况很有用。例如,'industr(?:y|ies)是比’industr|industries’更经济的表达式 |
(?=pattern) | 它是一个非捕获匹配。例如,'Windows(?=95|98|NT|2000)'匹配"Windows 2000"中的"Windows",但不匹配"Windows 3.1"中的"Windows". |
(?!pattern) | 该表达式匹配不处于匹配pattern的字符串的起始点的搜索字符串。它是一个非捕获匹配。例如,'Windos(?!95|98|NT|2000)'匹配"Windows 3.1"中的"Windows",但不匹配"Windows 2000"中的"Windows"。 |
- 捕获分组
package com.yyq.regexp;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* @title: RegExp02
* @Author yyq
* @Date: 2023/4/6 16:05
* @Version 1.0
* @description 分组
*/
public class RegExp02 {
public static void main(String[] args) {
String content = "yiyuqing s7789 nn 1189yyq";
//1.非命名捕获
String regStr = "(\\d\\d)(\\d\\d)";
//2.命名捕获
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));
System.out.println("第一个分组内容 " + matcher.group(1));
System.out.println("第二个分组内容 " + matcher.group(2));
}
*/
//命名捕获,也可以用编号
while (matcher.find()) {
System.out.println("找到 " + matcher.group(0));
System.out.println("第一个分组g1内容 " + matcher.group("g1"));
System.out.println("第二个分组g2内容 " + matcher.group("g2"));
}
}
}
- 非捕获分组
package com.yyq.regexp;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* @title: RegExp03
* @Author yyq
* @Date: 2023/4/6 16:29
* @Version 1.0
* @description 非捕获分组 ()里面pattern不会捕获,所以该写法是一个单组,只有一个组group(0),写group(1)会报错
*/
public class RegExp03 {
public static void main(String[] args) {
String content = "hello韩顺平教育 jack韩顺平老师 韩顺平同学hello";
String regStr;
//1.找到韩顺平教育、韩顺平老师、韩顺平同学子字符串
regStr = "韩顺平教育|韩顺平老师|韩顺平同学";
//和上面等价
regStr = "韩顺平(?:教育|老师|同学)";
//2.找到韩顺平这个关键字,要求只是查找韩顺平教育和韩顺平老师中包含的韩顺平
regStr = "韩顺平(?=教育|老师)";
//3.找到韩顺平这个关键字,要求只是查找不是(韩顺平教育和韩顺平老师)中包含的韩顺平
regStr = "韩顺平(?!教育|老师)";
Pattern pattern = Pattern.compile(regStr);
Matcher matcher = pattern.matcher(content);
while (matcher.find()) {
System.out.println("找到 " + matcher.group(0));
}
}
}
- 非贪婪匹配
package com.yyq.regexp;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* @title: RegExp04
* @Author yyq
* @Date: 2023/4/6 17:15
* @Version 1.0
* @description 非贪婪匹配
*/
public class RegExp04 {
public static void main(String[] args) {
String content = "hello11111 ok";
String regStr;
//默认贪婪匹配
regStr = "\\d+";
//非贪婪匹配
regStr = "\\d+?";
Pattern pattern = Pattern.compile(regStr);
Matcher matcher = pattern.matcher(content);
while (matcher.find()) {
System.out.println("找到 " + matcher.group(0));
}
}
}
正则表达式应用
- 案例一
package com.yyq.regexp;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* @title: RegExp05
* @Author yyq
* @Date: 2023/4/7 8:30
* @Version 1.0
* @description 正则表达式应用
*/
public class RegExp05 {
public static void main(String[] args) {
String content;
String regStr;
//1.汉字
content = "韩顺平教育";
regStr = "^[\u0391-\uffe5]+$";
//2.邮政编码,要求:是1-9开头的一个六位数。
content = "123564";
regStr = "^[1-9]\\d{5}$";
//3.QQ号,要求:是1-9开头的一个(5位数-10位数)
content = "546367602a";
regStr = "^[1-9]\\d{4,9}$";
//4.手机号,要求:必须以13,14,15,18开头的11位数
content = "15971208342";
regStr = "^1[3458]\\d{9}$";
regStr = "^1(?:3|4|5|8)\\d{9}$";
Pattern pattern = Pattern.compile(regStr);
Matcher matcher = pattern.matcher(content);
if (matcher.find()) {
System.out.println("找到:" + matcher.group(0));
}else{
System.out.println("不满足条件");
}
}
}
- 案例二
package com.yyq.regexp;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* @title: RegExp06
* @Author yyq
* @Date: 2023/4/7 9:12
* @Version 1.0
* @description 验证URL
*/
public class RegExp06 {
public static void main(String[] args) {
String content = "https://www.bilibili.com/video/BV1Eq4y1E79W?p=17&spm_id_from=pageDriver&vd_source=9c867d4264667cd620c3339e66e28ba6";
/*
思路:
1.开头部分 https:// | http:// 可以有也可以没有
2.然后确定域名 ([\w-]+\.)+[\w-]+
3.资源地址 (\/[\w-?=&/%.#]*)? 可以有可以没有
*/
//[]中特殊字符不用转义
String regStr = "^((http|https)://)?([\\w-]+\\.)+[\\w-]+(\\/[\\w-?=&/%.#]*)?$";
Pattern pattern = Pattern.compile(regStr);
Matcher matcher = pattern.matcher(content);
if (matcher.find()){
System.out.println("找到 " + matcher.group(0));
}else{
System.out.println("不满足要求!");
}
}
}
正则表达式三个常用类
Pattern类
-
常用方法
-
Pattern.matches(regStr, content)
-
返回值:boolean
参数一:正则表达式
参数二:匹配文本
作用:该方法将正则表达式整体与文本匹配,可以不用加^$定位符
-
package com.yyq.regexp;
import java.util.regex.Pattern;
/**
* @title: PatterMethod
* @Author yyq
* @Date: 2023/4/11 14:03
* @Version 1.0
* @description 演示matches方法,用于整体匹配,整体匹配可以不用^$定位符
*/
public class PatterMethod {
public static void main(String[] args) {
String content = "hello abc hello";
String regStr;
regStr = "hello";
regStr = "hello.*";
boolean matches = Pattern.matches(regStr, content);
System.out.println("整体匹配:" + matches);
}
}
Matcher类
方法 | 作用 |
---|---|
public int start() | 返回以前匹配的初始索引 |
public int start(int group) | 返回在以前的匹配操作期间,由给定组所捕获的子序列的初始索引 |
public int end() | 返回最后匹配字符之后的偏移量 |
public int end(int group) | 返回在以前的匹配操作期间,由给定组所捕获子序列的最后字符之后的偏移量 |
public boolean lookingAt() | 尝试将从区域开头的输入序列与该模式匹配 |
public boolean find() | 尝试查找与该模式匹配的输入序列的下一个子序列 |
public boolean find(int start) | 重置从匹配器,然后尝试查找该匹配该模式、从指定索引开始的输入序列的下一个子序列 |
public boolean matches() | 尝试将整个区域与模式匹配 |
public Matcher appendReplacement(StringBuffer sb, String replacement) | 实现非终端添加和替换步骤 |
public StringBuffer appendTail(StringBuffer sb) | 实现终端添加和替换步骤 |
public String replaceAll(String replacement) | 替换模式与给定替换字符串相匹配的输入序列的每个子序列 |
public String replaceFirst(String replacement) | 替换模式与给定替换字符串匹配的输入序列的第一个子序列 |
public static String quoteReplacement(String s) | 返回指定字符串的字面替换字符串。这个方法返回一个字符串,就像传递给Matcher类的appendReplacement方法一个字面字符串一样工作 |
反向引用
-
圆括号的内容被捕获后,可以在这个括号后被使用,从而写出一个比较实用的匹配模式,这个我们称为反向引用,这种引用既可以是在正则表达式内部,也可以是在正则表达式外部,内部反向引用\\分组号,外部反向引用$分组号
-
案例一
package com.yyq.regexp;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* @title: RegExp07
* @Author yyq
* @Date: 2023/5/1 8:09
* @Version 1.0 反向引用
* @description
*/
public class RegExp07 {
public static void main(String[] args) {
String content;
String regStr;
//1.匹配两个练习相同数字
content = "hello jack tom11 jack22 yyy xx";
regStr = "(\\d)\\1";
//2.匹配五个连续相同数字
content = "hello33333 jack tom11 jack22 yyy12345 xx";
regStr = "(\\d)\\1{4}";
//3.匹配千位与各位相同,百位与十位相同
content = "hello1221 jack2345 tom11 jack22 yyy2112 xx";
regStr = "(\\d)(\\d)\\2\\1";
/*
4.请在字符串中检索商品编号,形式如:12321-333999111这样的号码,
要求满足前面是一个五位数,然后一个-号,然后是一个九位数,连续的每三位要相同
*/
content = "hello jack tom11 jack22 yyy xx12321-333999111";
regStr = "\\d{5}-(\\d)\\1{2}(\\d)\\2{2}(\\d)\\3{2}";
Pattern pattern = Pattern.compile(regStr);
Matcher matcher = pattern.matcher(content);
while (matcher.find()) {
System.out.println("找到: " + matcher.group(0));
}
}
}
- 案例二
package com.yyq.regexp;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* @title: RegExp08
* @Author yyq
* @Date: 2023/5/1 8:59
* @Version 1.0
* @description
*/
public class RegExp08 {
public static void main(String[] args) {
//1.方法一
/*
String content = "我...我要...学学学学....编程Java!";
content = content.replace(".","");
String regStr = "(.)\\1*";
Pattern pattern = Pattern.compile(regStr);
Matcher matcher = pattern.matcher(content);
while (matcher.find()) {
System.out.println(matcher.group(0).charAt(0));
}
*/
//2.方法二
String content = "我...我要...学学学学....编程Java!";
String regStr = "\\.";
Pattern pattern = Pattern.compile(regStr);
Matcher matcher = pattern.matcher(content);
//2.1去掉.
content = matcher.replaceAll("");
/*
//2.2
pattern = Pattern.compile("(.)\\1+");
matcher = pattern.matcher(content);
while (matcher.find()) {
System.out.println("找到: " + matcher.group(0));
}
//2.3使用外部反向引用
content = matcher.replaceAll("$1");
System.out.println(content);
*/
//简化成一句代码
content = Pattern.compile("(.)\\1+").matcher(content).replaceAll("$1");
System.out.println(content);
}
}
章节练习
- 案例一
package com.yyq.regexp;
/**
* @title: Homework01
* @Author yyq
* @Date: 2023/5/2 13:02
* @Version 1.0
* @description
*/
public class Homework01 {
public static void main(String[] args) {
/*
规定电子邮箱规则为
1.只能有一个@
2.@前面用户名,可以是a-z A-Z 0-9 _ -字符
3.@后面是域名,并且域名只能是英文字母,比如souhu.com 或者tsinghua.org.cn
*/
String content = "qingsanxing@qq.cn";
String regStr = "^([\\w-])+@([a-zA-Z]+\\.)+[a-zA-Z]+$";
//该matchers是个整体匹配,内部其实调用Pattern.matches()方法,然后Pattern.matches()调用Matcher.matchers()方法
if (content.matches(regStr)) {
System.out.println("匹配成功!");
}else{
System.out.println("匹配失败!");
}
}
}
- 案例二
package com.yyq.regexp;
/**
* @title: HomeWork02
* @Author yyq
* @Date: 2023/5/2 13:39
* @Version 1.0
* @description
*/
public class HomeWork02 {
public static void main(String[] args) {
/*
要求是不是整数或者小数
*/
String content = "-0.003";
String regStr = "^([+-])?(0|[1-9]\\d*)(\\.\\d+)?$";
if (content.matches(regStr)) {
System.out.println("匹配成功:整数或者小数!");
}else {
System.out.println("匹配失败!");
}
}
}
- 案例三
package com.yyq.regexp;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* @title: Homework03
* @Author yyq
* @Date: 2023/5/2 14:13
* @Version 1.0
* @description
*/
public class Homework03 {
public static void main(String[] args) {
/*
http://www.sohu.com:8080/abc/index.html
a)要求得到协议是什么? http
b)域名是什么? www.sohu.com
c)端口是什么? 8080
d)文件名是什么? index.htm
*/
String content = "http://www.sohu.com:8080/abc/index.html";
String regStr = "^([a-zA-Z]+)://([a-zA-Z.]+):([1-9]\\d+)[\\w-/]*/([\\w.]+)$";
Pattern pattern = Pattern.compile(regStr);
Matcher matcher = pattern.matcher(content);
if (matcher.matches()) {
System.out.println("匹配成功:"+matcher.group(0));
System.out.println("协议:"+matcher.group(1));
System.out.println("域名:"+matcher.group(2));
System.out.println("端口号:"+matcher.group(3));
System.out.println("资源名:"+matcher.group(4));
}else{
System.out.println("匹配失败!");
}
}
}