- 正则表达式学习
- find() 和 group()
- 1. 正则表达式语法
- 1. 转义符 ``\``
- 2. 字符匹配符
- 3. 选择匹配符 |
- 4. 限定符
- 5. 定位符
- 6. 分组
- 7. 非贪婪匹配
- 8. 练习
- 2. 正则表达式三个常用类
- 1. Pattern类
- 2. Matcher类
- 3. PatternSyntaxException
- 3. 反向引用
- 4. String类中使用正则表达式
简单的说:正则表达式是对字符串执行模式匹配 的技术
正则表达式:regular expression — RegExp
find() 和 group()对matcher.find() 和 matcher.group() 部分过程的理解
package regexp;
import org.junit.jupiter.api.Test;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class FindBottom {
@Test
public void Find_Bottom(){
//匹配四个连着的数字 如1111,2346,....
String regStr = "(\d\d)(\d\d)";
String content = "1998年春天,我和我的父亲一起去北京,而2001年2月我和我的父亲去了上海";
Pattern pattern = Pattern.compile(regStr);
Matcher matcher = pattern.matcher(content);
while (matcher.find()) {
// 若正则表达式有分组()
// 取出匹配的字符串规则如下:
// groups[0] 表示匹配到的子字符串
// groups[1] 表示匹配到的子字符串第 1 组子串
// groups[2] 表示匹配到的子字符串第 2 组子串
// 以此类推... 但是不能越界
System.out.println(matcher.group(0));
System.out.println("第1组()匹配到的="+matcher.group(1));
System.out.println("第2组()匹配到的="+matcher.group(2));
}
}
}
1. 正则表达式语法
1. 转义符 \
在 Java中\ 表示其他语言中的
需要用到转义符号的字符:*+()$/?[]^{}.
package regexp;
import org.junit.jupiter.api.Test;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class Regex01 {
@Test
public void test(){
String content = "abc(def.gh";
String regStr = "\(";
String regStr2 = "\."; //匹配.字符
String regStr3 = "."; //匹配全部字符
Pattern pattern = Pattern.compile(regStr2);
Matcher matcher = pattern.matcher(content);
while (matcher.find()) {
System.out.println("匹配到"+matcher.group(0));
}
}
}
2. 字符匹配符
| 符号 | 作用 | 示例 | 解释 | 匹配输入 |
|---|---|---|---|---|
| 【】 | 可接收的字符列表 | 【efgh】 | e、f、g、h中的任意1个字符 | |
| 【^】 | 不接收的字符表 | 【^abc】 | 除a、b、c之外的任意一个字符,包括数字和特殊符号 | |
| - | 连字符 | A-Z | 任意单个大写字母 | |
| . | 匹配除n以外的任何字符 | a…b | 以a开头,b结尾,中间包括2个任意字符的长度为4的字符串 | aaab,aefb,a35b,a#*b |
| \d | 匹配单个数字字符,相当于【0-9】 | \d{3}{\d}? | 包含3个或4个数字的字符串 | 123、9876 |
| \D | 匹配单个非数字字符,相当于【^0-9】 | \D{\d}* | 以单个非数字字符开头,后接任意个数字字符串 | a、A342 |
| \w | 匹配单个数字、大小写字母字符、下划线,相当于【0-9a-zA-Z_】 | \d{3}\w{4} | 以3个数字字符开头的长度为7的数字字母字符串 | 234abcd、12345Pe |
| \W | 匹配单个非数字、大小写字母字符,相当于【^0-9a-zA-Z】 | \W+\d{2} | 以至少1个非数字字母字符开头,2个数字字符结尾的字符串 | #29、#?@10 |
| \s | 匹配任何空白字符(空格、制表符等) | |||
| \S | 匹配任何非空白字符,和\s相反 |
\d\d\d == \d{3}
+表示1个或多个
?表示0个或1个
*表示0个或多个
示例
package regexp;
import org.junit.jupiter.api.Test;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class Regex01 {
@Test
public void test(){
String content = "abcABCaBCAbcAbCabABcba";
String regStr0 = "abc";
String regStr1 ="(?i)abc"; //不区分 abc 大小写
String regStr2 = "a(?i)bc"; //表示 bc 不区分大小写
String regStr3 = "a((?i)b)c"; //表示 b 不区分大小写
Pattern pattern1 = Pattern.compile(regStr0,Pattern.CASE_INSENSITIVE); //不区分 abc 大小写
Matcher matcher0 = pattern1.matcher(content);
while (matcher0.find()) {
System.out.println("匹配到"+matcher0.group(0));
}
}
@Test
public void test2() {
String content = "abcAbc1234abc2a1";
String regStr = "(?i)[^abc]";
Pattern pattern = Pattern.compile(regStr);
//两种效果相同
// String regStr = "[^abc]";
// Pattern pattern = Pattern.compile(regStr, Pattern.CASE_INSENSITIVE);
Matcher matcher = pattern.matcher(content);
while (matcher.find()) {
System.out.println(matcher.group(0));
}
}
@Test
public void test3(){
String content = "a b c"; //GBK 编码情况下,一个空格,一个制表符tab
String regStr = "\s";
Pattern pattern = Pattern.compile(regStr);
Matcher matcher = pattern.matcher(content);
while (matcher.find()){
System.out.println("找到"+matcher.group(0));
}
}
}
3. 选择匹配符 |
| 符号 | 说明 | 实例 | 解释 |
|---|---|---|---|
| | | 匹配“|”之前或之后的表达式 | ab|cd | ab或cd |
@Test
public void test4(){
String content = "你好世界,hello world";
String regStr = "你|hello|ld";
Pattern pattern = Pattern.compile(regStr);
Matcher matcher = pattern.matcher(content);
while (matcher.find()){
System.out.println(matcher.group(0));
}
}
4. 限定符
跟在谁后面谁就要受到约束,若是一个整体则要跟到一个括号后面
| 符号 | 含义 | 示例 | 说明 | 匹配输入 |
|---|---|---|---|---|
| * | 指定字符重复0次或n次 | (abc)* | 仅包含任意个abc的字符 | abc、abcabcabc |
| + | 指定字符重复1次或n次(至少1次) | m+(abc)* | 以至少1个m开头,后接任意个abc的字符串 | m、mabc、mabcabc |
| ? | 指定字符重复0次或1次(最多1次) | m+abc? | 以至少1个m开头,后接ab或abc的字符串 | mab、mabc、mmmab、mmmmabc |
| {n} | 指定只能出现n个匹配字符 | 【abcd】{3} | 由abcd中字母组成的任意长度为3的字符串 | abc、dbc、adc |
| {n,} | 指定至少n个匹配字符 | 【abcd】{3,} | 由abcd中的字母组成的任意长度不小于3的字符串 | aab、bdc、aaabdc |
| {n,m} | 指定至少n个但不多于m个匹配字符 | 【abcd】{3,5} | 由abcd中字母组成的任意长度不小于3,不大于5的字符串 | abc、abcd、aaaa、bcdab |
@Test
public void test5() {
String content = "mmabcabcamabcabcabcm";
String regStr = "m+(abc)*";
Pattern pattern = Pattern.compile(regStr);
Matcher matcher = pattern.matcher(content);
while (matcher.find()){
System.out.println(matcher.group(0));
}
String content2 = "aaaaab";
String regStr2 = "a{3,5}"; //a出现至少3次,至多5次
Pattern pattern2 = Pattern.compile(regStr2);
Matcher matcher2 = pattern2.matcher(content2);
while (matcher2.find()){
System.out.println(matcher2.group(0));
}
String content3 = "a11aaa1ab";
String regStr3 = "a1+"; //a开头,1至少重复1次
Pattern pattern3 = Pattern.compile(regStr3);
Matcher matcher3 = pattern3.matcher(content3);
while (matcher3.find()){
System.out.println(matcher3.group(0));
}
String content4 = "a11aaa1ab";
String regStr4 = "a1*"; //a开头,1出现0-n次
Pattern pattern4 = Pattern.compile(regStr4);
Matcher matcher4 = pattern3.matcher(content4);
while (matcher4.find()){
System.out.println(matcher4.group(0));
}
}
5. 定位符
| 符号 | 含义 | 示例 | 说明 | 匹配输入 |
|---|---|---|---|---|
| ^ | 匹配输入字符串的开始位置(指定起始字符 | 1+[a-z]* | 以至少1个数字开头,后接任意个小写字母的字符串 | 123,6aa,555edf |
| $ | 指定结束字符 | 2\-[a-z]+$ | 以1个数字开头后接连字符“-”,并以至少1个小写字母结尾的字符串 | 1-a |
| \b | 匹配目标字符串的边界 | han\b | 这里说的字符串的边界是子串间有空格,或者是目标字符串的结束位置 | sphan nnhan hanshunping(这里han是在开头所以没有匹配到) |
| \B | 匹配目标字符串的非边界 | han\B | 与\b含义相反 | hanshunping nnhan(同理这个结尾的han没有匹配到) |
package regexp;
import org.junit.jupiter.api.Test;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class Regex02 {
@Test
public void test01(){
String content = "123abc";
String regStr ="^[0-9]+[a-z]*";
Pattern pattern = Pattern.compile(regStr);
Matcher matcher = pattern.matcher(content);
while (matcher.find()) {
System.out.println("找到 ->"+matcher.group(0));
}
//注意:若content = “ a123abc ” 则什么都没有匹配成功
}
@Test
public void test02(){
String content = "hannn nnhan xhanx";
String regStr = "han\b"; //边界既可以是有空格处的,也可以是字符串的末尾处
Pattern pattern = Pattern.compile(regStr);
Matcher matcher = pattern.matcher(content);
while (matcher.find()) {
System.out.println(matcher.group(0));
}
System.out.println("--------------");
String regStr2 = "han\B";
pattern = Pattern.compile(regStr2);
matcher = pattern.matcher(content);
while (matcher.find()) {
System.out.println(matcher.group(0));
}
}
}
6. 分组
| 常用分组构造形式 | 说明 |
|---|---|
| (pattern) | 非命名捕获,捕获匹配的子字符串,编号为0的第一个捕获是由整个正则表达式模式匹配的文本,其它捕获结果则根据左括号的顺序从1开始自动编号 |
| (?pattern) | 命名捕获,将匹配的字符串捕获到一个组名称或编号名称中。用于name的字符串不能包含任何标点符号,并且不能以数字开头,可以使用单引号替代尖括号,例如(?‘name’) |
特别分组
| 常用分组构造形式 | 说明 |
|---|---|
| (?:pattern) | 匹配pattern但不捕获该匹配的子表达式,即它是一个非捕获匹配,不存储以后使用的匹配,这对于”or“字符(|)组合模式部件的情况很有用。例如 ‘indutr(?|ies)’ 是比 ‘industry|industries’ 更经济的表达式 |
| (?=pattern) | 它是一个非捕获匹配。例如 ‘Windows(?=95|98|NT|2000)’ 匹配"Windows2000"中的"Windows",但不匹配 “Windows3.1"中的"Windows” |
| (?!pattern) | 该表达式匹配不处于匹配 pattern 的字符串的起始点的搜索字符串。它是一个非捕获匹配。例如 “Windows(?!95|98|NT|2000)” 匹配 “Windows3.1"中的 “Windows”,但不匹配"Windows2000"中的"Windows” |
package regexp;
import org.junit.jupiter.api.Test;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class Regex03 {
@Test
public void test(){
String content = "hello world 2022年5月12345678";
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));
System.out.println("第一组的子字符串:"+matcher.group("arr1"));
System.out.println("第二组的子字符串:"+matcher.group("arr2"));
System.out.println();
}
}
}
@Test
public void test2(){
String content = "小明 老师hhh小明同学xxx小明教育hello,今日头条,热搜榜第一是...";
String regStr = "小明(?:老师|同学|教育)";
Pattern pattern = Pattern.compile(regStr);
Matcher matcher = pattern.matcher(content);
while (matcher.find()) {
System.out.println(matcher.group(0)); //true
// System.out.println(matcher.group(1)); //error,由于是不捕获的,所以无法使用索引去获取
}
}
更多元字符可以去网上查询
7. 非贪婪匹配@Test
public void test3(){
String content = "hello111111";
String regStr = "\d+"; //默认贪婪匹配
String regStr2 = "\d+?"; //设置成非贪婪匹配
Pattern pattern = Pattern.compile(regStr);
Matcher matcher = pattern.matcher(content);
while (matcher.find()) {
System.out.println(matcher.group(0));
}
Pattern pattern1 = Pattern.compile(regStr2);
Matcher matcher1 = pattern1.matcher(content);
while (matcher1.find()){
System.out.println(matcher1.group(0));
}
}
8. 练习
package regexp;
import org.junit.jupiter.api.Test;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class Works {
@Test
public void Check_Chinese_charactersChild() {
String content = "你好世界";
String regStr = "^[u4e00-u9fa5]+$"; //这是汉字的编码范围
Pattern pattern = Pattern.compile(regStr);
Matcher matcher = pattern.matcher(content);
if(matcher.find()){
System.out.println("满足格式");
}else {
System.out.println("不满足格式");
}
}
@Test
public void Check_phoneNumber(){
String content = "13911111111";
String regStr = "^1[3458]\d{9}"; //"^1[3|4|5|8]\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("满足格式");
}else {
System.out.println("不满足格式");
}
}
@Test
public void Check_url(){
String content = "https://leetcode-cn.com/problems/minimum-genetic-mutation/";
String regStr = "^(https?://)?([\w-]+\.)+[\w-]+(/[\w-?%=#&/.]*)?$";
Pattern pattern = Pattern.compile(regStr);
Matcher matcher = pattern.matcher(content);
if (matcher.find()) {
System.out.println("满足格式");
}else {
System.out.println("不满足格式");
}
}
@Test
public void tests(){
String content = "a b c";
String regStr ="."; //匹配除了n的所有字符
Pattern pattern = Pattern.compile(regStr);
Matcher matcher = pattern.matcher(content);
while (matcher.find()) {
System.out.println(matcher.group(0));
}
}
}
2. 正则表达式三个常用类
1. Pattern类
pattern对象是一个正则表达式对象。Pattern类没有公共构造方法。要创建一个Pattern对象,调用其公共静态方法,它返回一个Pattern对象。该方法接受一个正则表达式作为它的第一个参数,如 Pattern p = Pattern.compile(regStr)
2. Matcher类Matcher对象是对输入字符串进行解释和匹配的引擎。与Pattern类一样,Matcher也没有公共的构造方法。你需要调用Pattern对象的matcher方法 来获取一个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() 尝试将整个区域与模式匹配 |
package regexp;
import org.junit.jupiter.api.Test;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class Regex05 {
@Test
public void test1(){
String content = "hello world,hello Java,hello C";
String regStr = "hello";
Pattern pattern = Pattern.compile(regStr);
Matcher matcher = pattern.matcher(content);
while (matcher.find()) {
System.out.println("索引开始位置:"+matcher.start());
System.out.println("索引结束位置:"+matcher.end());
System.out.println(content.substring(matcher.start(), matcher.end()));
System.out.println("===================");
}
//整体匹配matches() 方法常用于校验某个字符串是否满足某个规划
System.out.println("整体匹配-->"+matcher.matches()); //false
System.out.println("========================");
//完成如果content含有Java,则替换成Golang
regStr = "Java";
pattern = Pattern.compile(regStr);
matcher = pattern.matcher(content);
//注意:replaceAll 返回的字符串才是替换后的字符串,原来的字符串不会变化
String newContent = matcher.replaceAll("Golang");
System.out.println("替换后的content:"+newContent);
System.out.println("替换前的content:"+content);
pattern = Pattern.compile("\d+");
matcher = pattern.matcher("123abc");
boolean b = matcher.lookingAt();
System.out.println(b); //true,因为lookingAt是对要匹配的字符串前面部分进行匹配,如果前面部分有 规定的模式,则返回true否则返回false
matcher = pattern.matcher("abc123");
boolean b1 = matcher.lookingAt();
System.out.println(b1); //false
}
}
Pattern.matches()方法
package regexp;
import org.junit.jupiter.api.Test;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class Regex04 {
@Test
public void test1(){
String content = "hello world,I am coming!";
String regStr = "hello";
boolean matches = Pattern.matches(regStr, content);
//这里matches(regStr,content)等同于
// Pattern pattern = Pattern.compile("^(hello)$");
// Matcher matcher = pattern.matcher(content);
// boolean matches = matcher.matches();
System.out.println("整体匹配-->"+matches); //整体匹配-->false
regStr = "hello.*";
boolean matches1 = Pattern.matches(regStr, content);
System.out.println("整体匹配-->"+matches1); //整匹配-->true
}
}
3. PatternSyntaxException
PatternSyntaxException 是一个非强制异常类,它表示一个正则表达式模式中的语法错误
3. 反向引用
@Test
public void Back_reference(){
String content = "1221 1234 2442 1111 8919 1882 1891";
// 分组() ,分组以括号括起来,组号从1开始
// 以分组 捕获的形式,反向引用第一组 \1 反向引用第二组 \2
String regStr = "(\d)(\d)\2\1";
Pattern pattern = Pattern.compile(regStr);
Matcher matcher = pattern.matcher(content);
while (matcher.find()) {
System.out.println("匹配到 "+matcher.group(0));
}
content = "12321-333999111 11111-111222333 12345-222333444 1331-221133 1221-11122111 4554-22221133";
String regStr2 = "(\d)(\d)\d\2\1-(\d)\3{2}(\d)\4{2}(\d)\5{2}";
Pattern pattern2 = Pattern.compile(regStr2);
Matcher matcher2 = pattern2.matcher(content);
while (matcher2.find()) {
System.out.println("匹配到 "+matcher2.group(0));
}
}
结巴 程序
@Test
public void Stutter_procedure(){
String content = "我我我要要要学学编程程";
StringBuilder newContent = new StringBuilder();
String regStr = "([u4e00-u9fa5])\1*";
Pattern pattern = Pattern.compile(regStr);
Matcher matcher = pattern.matcher(content);
while (matcher.find()) {
newContent.append(matcher.group(1));
}
System.out.println(newContent);
}
@Test
public void Stutter_procedure2(){
String content = "我...我我要..要要学学...编程....程";
String regStr = "\.";
Pattern pattern = Pattern.compile(regStr);
Matcher matcher = pattern.matcher(content);
content = matcher.replaceAll("");
regStr = "(.)\1+";
pattern = Pattern.compile(regStr);
matcher = pattern.matcher(content);
//外部(非正则表达式regStr)使用分组时,要用 $
content = matcher.replaceAll("$1");
System.out.println(content);
}
@Test
public void Stutter_procedure3(){
String content = "我...我我要..要要学学...编程....程";
content = Pattern.compile("\.").matcher(content).replaceAll("");
content = Pattern.compile("(.)\1+").matcher(content).replaceAll("$1");
System.out.println(content);
}
4. String类中使用正则表达式
方便之处:不用再构造一个Pattern和Matcher了
替换功能:
String 类的 public String replaceAll(String regex,String replacement)
@Test
public void StringReplace1(){
String content = "1996年1月,Sun公司发布了Java的第一个开发工具包(JDK 1.0),这是Java发展历程中的重要里程碑,标志着Java成为一种独立的开发工具。9月,约8.3万个网页应用了Java技术来制作。10月,Sun公司发布了Java平台的第一个即时(JIT)编译器。n" +
"1997年2月,JDK 1.1面世,在随后的3周时间里,达到了22万次的下载量。4月2日,Java One会议召开,参会者逾一万人,创当时全球同类会议规模之纪录。9月,Java Developer Connection社区成员超过10万。n" +
"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发展过程中最重要的一个里程碑,标志着Java的应用开始普及。";
//使用正则表达式 将Java 和 J2EE 替换成 JavaavaJ
content = content.replaceAll("Java|J2EE","JavaavaJ");
System.out.println("替换后:"+content);
}
判断功能:
String 类的 public boolean matches(String regex)
@Test
public void Check_phoneNumber(){
String content = "13888888888";
if(content.matches("1(?:38|39)\d{8}")){
System.out.println("验证成功");
}else {
System.out.println("验证失败");
}
}
分割功能:
String 类的 public String[] split(String regex)
@Test
public void Splits(){
String content = "hello#abc-tom99jerry~北京";
String[] split = content.split("-|#|~|\d+");//用 - # ~ d+ 进行分割字符串
for(String it:split){
System.out.println(it);
}
}
0-9 ↩︎
0-9 ↩︎



