文章目录
- [1 正则表达式基本介绍](#1 正则表达式基本介绍)
- [2 正则表达式语法](#2 正则表达式语法)
-
- [2.1 元字符-转义号 \\\](#2.1 元字符-转义号 \)
- [2.2 元字符-字符匹配符](#2.2 元字符-字符匹配符)
- [2.3 元字符-选择匹配符](#2.3 元字符-选择匹配符)
- [2.4 元字符-限定符](#2.4 元字符-限定符)
- [2.5 元字符-定位符](#2.5 元字符-定位符)
- [2.6 分组](#2.6 分组)
- [3 正则表达式三个常用类](#3 正则表达式三个常用类)
-
- [3.1 Pattern类的Matches方法](#3.1 Pattern类的Matches方法)
- [3.2 Matcher类的常用方法](#3.2 Matcher类的常用方法)
- [4 分组、捕获、反向引用](#4 分组、捕获、反向引用)
- [5 String类中使用正则表达式](#5 String类中使用正则表达式)
-
- [5.1 替换功能](#5.1 替换功能)
- [5.2 判断功能](#5.2 判断功能)
- [5.3 分割功能](#5.3 分割功能)
- [6 正则表达式使用案例](#6 正则表达式使用案例)
-
- [6.1 验证电子邮箱格式是否合法](#6.1 验证电子邮箱格式是否合法)
- [6.2 验证手机号码是否合法](#6.2 验证手机号码是否合法)
- [6.3 验证URL格式是否合法](#6.3 验证URL格式是否合法)
- [7 正则表达式底层实现](#7 正则表达式底层实现)
- [8 Reference](#8 Reference)
1 正则表达式基本介绍
正则表达式(Regular Expression),简单地说:正则表达式是对字符串执行模式匹配的技术。
一个正则表达式,就是用某种模式去匹配字符串的一个公式。
2 正则表达式语法
如果要想灵活的运用正则表达式,必须了解其中各种元字符的功能,元字符从功能上大致分为:
- 限定符
- 选择匹配符
- 分组组合和反向引用符
- 特殊字符
- 字符匹配符
- 定位符
2.1 元字符-转义号 \\
在我们使用正则表达式去检索某些特殊字符的时候,需要用到转义符号,否则检索不到结果,甚至会报错的。
需要用到转义符号的字符有以下:
. * + () $ / \ ? [ ] ^ { }
2.2 元字符-字符匹配符
符号 | 含义 | 示例 | 解释 | 匹配输入 |
---|---|---|---|---|
[ ] | 可接收字符列表 | [abcd] | a、b、c、d中任意1个字符 | a,b,c,d |
[^] | 不接收字符列表 | [^abc] | 除a、b、c之外的任意一个字符 | d,1,#,... |
- | 连字符 | a-z | 任意单个小写字母 | a,d,... |
. | 匹配除\n以外的的任何字符 | a...b | 以a开头,b结尾,中间包括2个任意字符的长度为4的字符串 | a12b,aw@b,... |
\\d | 匹配单个数字字符,相当于[0,9] | \\d{3}(\\d)? | 包含3个或4个数字的字符串 | 123,1234,... |
\\D | 匹配单个非数字字符,相当于[ ^0-9] | \\D(\\d)* | 以单个非数字字符开头,后接任意个数字字符串 | q,#,A123,... |
\\w | 匹配单个数字、大小写字母、下划线,相当于[0-9a-zA-Z_] | \\d{3}\\w{4} | 以3个数字开头的长度为7的数字字母字符串 | 123abcd,12345ab,1234567 |
\\W | 匹配单个非数字、大小写字母、下划线字符,相当于[ ^0-9a-zA-Z] | \\W+\\d{2} | 以至少1个非数字字母字符开头,2个数字字符结尾的字符串 | #12,#*&@90,... |
\\s | 匹配任意空白字符(空格,制表符) | \\s | ||
\\S | 匹配任意非空白字符(空格,制表符) | \\S |
2.3 元字符-选择匹配符
符号 | 含义 | 示例 | 解释 | 匹配输入 |
---|---|---|---|---|
| | 匹配"|"之前或之后的表达式 | ab|cd | ab或者cd | ab,cd |
2.4 元字符-限定符
用于指定前面的字符和组合项连续出现多少次
符号 | 含义 | 示例 | 解释 | 匹配输入 |
---|---|---|---|---|
* | 指定字符重复0次或n次(0到多) | (abc)* | 仅包含任意个abc的字符串 | abc,abcabc,... |
+ | 指定字符重复1次或n次(1到多) | A+(ab)* | 以至少1个A开头,后接任意个ab的字符串 | Aab,AAabab |
? | 指定字符重复0次或1次(0到1) | A+abc? | 以至少1个A开头,后接ab或abc的字符串 | Aab,AAabc |
{n} | 只能输入n个字符 | [abcd]{3} | 由abcd中字母组成任意长度为3的字符串 | abc,acd,... |
{n,} | 指定至少n个匹配 | [abcd]{3,} | 由abcd中字母组成任意长度不小于3的字符串 | aab,abcd,aabbcd |
{n,m} | 指定至少n个但不多于m个匹配 | [abcd]{3,5} | 由abcd中字母组成任意长度不小于3,不大于5的字符串 | abc,abcd,aaaaa |
2.5 元字符-定位符
符号 | 含义 | 示例 | 解释 | 匹配输入 |
---|---|---|---|---|
^ | 指定起始字符 | ^ [0-9]+[a-z]* | 以至少1个数字开头,后接任意个小写字母的字符串 | 123,1aa,22esf |
$ | 指定结束字符 | ^ [0-9]\\-[a-z]+$ | 以1个数字开头后接连字符"-",并以至少1个小写字母结尾的字符串 | 1-a,1-abc |
\\b | 匹配目标字符串的边界 | abc\\b | 这里的字符串的边界指的是子串间有空格,或者是目标字符串的结束位置 | qwerabc |
\\B | 匹配目标字符串的非边界 | abc\\B | 和\b的含义相反 | abcqwer |
2.6 分组
常用分组构造形式 | 说明 |
---|---|
(pattern) | 非命名捕获。捕获匹配的子字符串。编号为0的第一个捕获是由整个正则表达式模式匹配的文本,其它捕获结果则根据左括号的顺序从1开始自动编号 |
(?pattern) | 命名捕获。将匹配的字符串捕获到一个组名或编号名称中。用于name的字符串不能包含任何标点符号,并且不能以数字开头。可以使用单引号代替尖括号,例如(?'name') |
(?:pattern) | 匹配pattern但不捕获该匹配的子表达式,即它是一个非捕获匹配,不存储以后使用的匹配。这对于用"or"字符(|)组合模式部件的情况很有用。例如,'industr(?:y|ies)'是比'industry|industries'更经济的表达式 |
(?=pattern) | 它是一个非捕获匹配。例如'Windows(?=95|98|NT|2000)匹配"Windows 2000"中的"Windows",但不匹配"Windows 10"中的"Windows" |
(?!pattern) | 该表达式匹配不处于匹配pattern的字符串的起始点的搜索字符串。它是一个非捕获匹配。例如'Windows(?=95|98|NT|2000)匹配"Windows 10"中的"Windows",但不匹配"Windows 2000"中的"Windows" |
java
package regularexpression;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* @package: regularexpression
* @description:分组
* @author: Yunyang
* @date: 2023/11/26 20:52
* @version:1.0
**/
public class RegExp07 {
public static void main(String[] args) {
String content = "helloworld s7789 n1189han";
// String regStr = "(\\d\\d)(\\d\\d)";//匹配4个数字的字符串
// String regStr = "(\\d\\d)(\\d)(\\d)";//匹配4个数字的字符串
String regStr = "(?<g1>\\d\\d)(?<g2>\\d\\d)";//匹配4个数字的字符串
Pattern pattern = Pattern.compile(regStr);
Matcher matcher = pattern.matcher(content);
while(matcher.find()){
System.out.println("找到:"+matcher.group(0));
System.out.println("找到第1个分组:"+matcher.group(1));
System.out.println("找到第1个分组[通过组名]:"+matcher.group("g1"));
System.out.println("找到第2个分组:"+matcher.group(2));
System.out.println("找到第2个分组[通过组名]:"+matcher.group("g2"));
// System.out.println("找到第3个分组:"+matcher.group(3));
}
}
}
3 正则表达式三个常用类
java.util.regex包主要包括以下三个类:Pattern类、Matcher类和PatternSyntaxException类
- Pattern类
pattern对象是一个正则表达式对象。Pattern类没有公共构造方法。要创建一个Pattern对象,调用其公共静态方法,它返回一个Pattern对象。
- Matcher类
Matcher对象是对输入字符串进行解释和匹配的引擎。与Pattern一样,Matcher也没有公告构造方法。需要调用Pattern对象的matcher方法来获得一个Matcher对象。
- PatternSyntaxException类
PatternSyntaxException是一个非强制异常类,它表示一个正则表达式模式下的语法错误。
3.1 Pattern类的Matches方法
java
package regularexpression;
import java.util.regex.Pattern;
/**
* @package: regularexpression
* @description:matches方法,用于整体匹配,在研制输入的字符串是否满足条件使用
* @author: Yunyang
* @date: 2023/11/26 22:03
* @version:1.0
**/
public class PatternMethod {
public static void main(String[] args) {
String content = "hello world hello java,你好世界";
String regStr = "hello.*";
boolean matches = Pattern.matches(regStr, content);
System.out.println("整体匹配 = " + matches);
}
}
3.2 Matcher类的常用方法
java
package regularexpression;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* @package: regularexpression
* @description:Matcher类的常用的方法
* @author: Yunyang
* @date: 2023/11/26 22:10
* @version:1.0
**/
public class MatcherMethod {
public static void main(String[] args) {
String content = "hello world jack hello tom hello smitch 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());
regStr = "world";
pattern = Pattern.compile(regStr);
matcher = pattern.matcher(content);
String newcontent = matcher.replaceAll("世界");
//注意:返回的字符串才是替换后的字符串 原来的 content 不变化
System.out.println("newcontent = " + newcontent);
System.out.println("content = " + content);
}
}
4 分组、捕获、反向引用
- 分组
我们可以用圆括号组成一个比较复杂的匹配模式,那么一个圆括号的部分就可以看作是一个子表达式/一个分组
例如:(\\d\\d)(\\d\\d)中有两个分组
- 捕获
把正则表达式/分组匹配的内容,保存到内存中以数字编号或显式命名的组里,方便后面引用,从左到右,以分组的左括号为标志,第一个出现的分组的组号为1,第二个为2,以此类推。组0表示的是整个正则表达式。
例如:(\\d\\d)(\\d\\d)(\\d)中有三个分组,组0表示整个表达式:(\\d\\d)(\\d\\d),组1表示前一个圆括号的内容(\\d\\d),组2表示第二个括号的内容(\\d\\d),组3表示最后一个括号的内容
- 反向引用
圆括号的内容被捕获后,可以在这个括号后被使用,从而写出一个比较实用的匹配模式,称之为反向引用,这种引用既可以是正则表达式内部,也可以是在正则表达式外部,内部反向引用**\\分组号**,外部反向引用**$分组号**
例如:匹配5个连续的相同数字:(\\d)\\1{4}
5 String类中使用正则表达式
5.1 替换功能
String 类 public String replaceAll(String regex,String replacement)
5.2 判断功能
String 类 public boolean matches(String regex){} //使用 Pattern 和 Matcher 类
5.3 分割功能
String 类 public String[] split(String regex)
java
package regularexpression;
/**
* @package: regularexpression
* @description:String类中使用正则表达式
* @author: Yunyang
* @date: 2023/11/26 23:08
* @version:1.0
**/
public class StringReg {
public static void main(String[] args) {
String content = "2000年5月,JDK1.3、JDK1.4和J2SE1.3相继发布,几周后其获得了Apple公司Mac OS X的工业标准的支持。2001年9月24日,J2EE1.3发布。2002年2月26日,J2SE1.4发布。自此Java的计算能力有了大幅提升,与J2SE1.3相比,其多了近62%的类和接口。在这些新特性当中,还提供了广泛的XML支持、安全套接字(Socket)支持(通过SSL与TLS协议)、全新的I/OAPI、正则表达式、日志与断言。2004年9月30日,J2SE1.5发布,成为Java语言发展史上的又一里程碑。为了表示该版本的重要性,J2SE 1.5更名为Java SE 5.0(内部版本号1.5.0),代号为"Tiger",Tiger包含了从1996年发布1.0版本以来的最重大的更新,其中包括泛型支持、基本类型的自动装箱、改进的循环、枚举类型、格式化I/O及可变参数。";
//使用正则表达式将JDK1.3和JDK1.4替换成JDK
content = content.replaceAll("JDK1\\.3|JDK1\\.4","JDK");
System.out.println(content);
//要求 验证一个 手机号, 要求必须是以 138 139 开头的
content = "13988889999";
if (content.matches("13(8|9)\\d{8}")) {
System.out.println("验证成功");
} else {
System.out.println("验证失败");
}
//要求按照 # 或者 - 或者 ~ 或者 数字 来分割
System.out.println("===================");
content = "hello#abc-jack12smith~北京";
String[] split = content.split("#|-|~|\\d+");
for (String s : split) {
System.out.println("s = " + s);
}
}
}
6 正则表达式使用案例
6.1 验证电子邮箱格式是否合法
java
package regularexpression;
/**
* @package: regularexpression
* @description:验证电子邮件格式是否合法
* @author: Yunyang
* @date: 2023/12/3 21:31
* @version:1.0
**/
public class CheckEmail {
public static void main(String[] args) {
// String content = "hsp@shun.cn.com";
String content = "2834660387@qq.com.cn";
String regStr = "^[\\w-]+@([a-zA-Z]+\\.)+[a-zA-Z]+$";
// String 的matches是整体匹配
if(content.matches(regStr)){
System.out.println("匹配成功");
} else {
System.out.println("匹配失败");
}
}
}
6.2 验证手机号码是否合法
要求: 必须以 13,14,15,18 开头的 11 位数 , 比如 13588889999
java
package regularexpression;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* @package: regularexpression
* @description:验证手机号码是否合法
* @author: Yunyang
* @date: 2023/11/26 21:20
* @version:1.0
**/
public class CheckPhone {
public static void main(String[] args) {
// 要求: 必须以 13,14,15,18 开头的 11 位数 , 比如 13588889999
String content = "13588889999";
String regStr = "^1[3|4|5|8]\\d{9}$";
Pattern pattern = Pattern.compile(regStr);
Matcher matcher = pattern.matcher(content);
if(matcher.find()){
System.out.println("满足格式");
} else {
System.out.println("不满足格式");
}
System.out.println(Pattern.matches(regStr, content));
}
}
6.3 验证URL格式是否合法
java
package regularexpression;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* @package: regularexpression
* @description:正则表达式的应用:url
* @author: Yunyang
* @date: 2023/11/26 21:42
* @version:1.0
**/
public class RegExp11 {
public static void main(String[] args) {
// String content = "https://www.bilibili.com/video/BV1Eq4y1E79W?p=17&spm_id_from=pageDriver";
String content = "https://docs.qq.com/doc/DR1NvRkNFU2ZpWGxQ?u=fc936b78ceee42de9e76d148895f84f3";
/*
* 思路:
* 1.先确定url的开始部分 http | https://
* 2.然后通过([\w-]+\.)+[\w-]+ 匹配www.bilibili.com
* 3.3. /video/BV1fh411y7R8?from=sear 匹配(\/[\w-?=&/%.#]*)?
*
* */
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("不满足格式");
}
}
}
7 正则表达式底层实现
java
package regularexpression;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* @package: regularexpression
* @description:分析Java正则表达式的底层实现
* @author: Yunyang
* @date: 2023/11/26 17:28
* @version:1.0
**/
public class RegTheory {
public static void main(String[] args) {
String content = "1998年12月8日,第二代Java平台的企业版J2EE发布。1999年6月,Sun公司发布了第二代Java平台(简称为Java2)的3个版本:J2ME(Java2 Micro Edition,Java2平台的微型版),应用于移动、无线及有限资源的环境;J2SE(Java 2 Standard Edition,Java 2平台的标准版),应用于桌面环境;J2EE(Java 2Enterprise Edition,Java 2平台的企业版),应用于基于Java的应用服务器。Java 2平台的发布,是Java发展过程中最重3443要的一个里程碑,标志着Java的应用开始普及9889。";
//匹配所有四个数字
//1.\\d表示一个数字
String regStr = "(\\d\\d)(\\d\\d)";
//2.创建正则表达式对象
Pattern pattern = Pattern.compile(regStr);
//3.创建匹配器
Matcher matcher = pattern.matcher(content);
//4.开始匹配
/**matcher.find()完成的任务 (考虑分组):
* 什么是分组,比如 (\d\d)(\d\d) ,正则表达式中有() 表示分组,第 1 个()表示第 1 组,第 2 个()表示第 2 组...
*1.根据指定的规则,定位满足规则的子字符串(比如1998)
*2.找到后,将 子字符串的开始的索引记录到 matcher 对象的属性 int[] groups;
* 2.1 groups[0] = 0 , 把该子字符串的结束的索引+1 的值记录到 groups[1] = 4
* 2.2 记录 1 组()匹配到的字符串 groups[2] = 0 groups[3] = 2
* 2.3 记录 2 组()匹配到的字符串 groups[4] = 2 groups[5] = 4
* 2.4 .如果有更多的分组.....
* 3.同时记录 oldLast 的值为 子字符串的结束的 索引+1 的值即 35, 即下次执行 find 时,就从 35 开始匹配
*
*
* 源码:
* public String group(int group) {
* if (first < 0)
* throw new IllegalStateException("No match found");
* if (group < 0 || group > groupCount())
* throw new IndexOutOfBoundsException("No group " + group);
* if ((groups[group*2] == -1) || (groups[group*2+1] == -1))
* return null;
* return getSubSequence(groups[group * 2], groups[group * 2 + 1]).toString();
* }
从代码中可以看出,数组中的每个分组的信息由两个连续的元素表示:一个是分组的开始位置,另一个是结束位置。因此,对于给定 的 group,它的开始和结束位置分别是 groups[group*2] 和 groups[group*2+1],检查发现开始或结束位置为-1,则返 回 null,表示该分组在匹配的字符串中不存在
*
* 1.根据group[0]=31 和groups[1]=35的记录的位置,从 content 开始截取子字符串返回
* 就是 [31,35) 包含 31 但是不包含索引为 35 的位置
*
* 如果再次指向 find 方法.仍然按照上面分析来执行
*/
while (matcher.find()) {
//小结
//1. 如果正则表达式有() 即分组
//2. 取出匹配的字符串规则如下
//3. group(0) 表示匹配到的子字符串
//4. group(1) 表示匹配到的子字符串的第一组字串
//5. group(2) 表示匹配到的子字符串的第 2 组字串
//6. ... 但是分组的数不能越界.
System.out.println("找到 " + matcher.group(0));
System.out.println("第1组()匹配到的值=" + matcher.group(1));
System.out.println("第2组()匹配到的值=" + matcher.group(2));
}
}
}
8 Reference
-
【韩顺平讲Java】Java 正则表达式专题 -正则 正则表达式 元字符 限定符 Pattern Matcher 分组 捕获 反向引用等
-
《正则表达式必知必会》 作者: [美] Ben Forta 著 , 杨涛 译