正则表达式

文章目录

  • [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 分组、捕获、反向引用

  1. 分组

我们可以用圆括号组成一个比较复杂的匹配模式,那么一个圆括号的部分就可以看作是一个子表达式/一个分组

例如:(\\d\\d)(\\d\\d)中有两个分组

  1. 捕获

把正则表达式/分组匹配的内容,保存到内存中以数字编号或显式命名的组里,方便后面引用,从左到右,以分组的左括号为标志,第一个出现的分组的组号为1,第二个为2,以此类推。组0表示的是整个正则表达式。

例如:(\\d\\d)(\\d\\d)(\\d)中有三个分组,组0表示整个表达式:(\\d\\d)(\\d\\d),组1表示前一个圆括号的内容(\\d\\d),组2表示第二个括号的内容(\\d\\d),组3表示最后一个括号的内容

  1. 反向引用

圆括号的内容被捕获后,可以在这个括号后被使用,从而写出一个比较实用的匹配模式,称之为反向引用,这种引用既可以是正则表达式内部,也可以是在正则表达式外部,内部反向引用**\\分组号**,外部反向引用**$分组号**

例如:匹配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

  1. 【韩顺平讲Java】Java 正则表达式专题 -正则 正则表达式 元字符 限定符 Pattern Matcher 分组 捕获 反向引用等

  2. 菜鸟教程:Java正则表达式

  3. 《正则表达式必知必会》 作者: Ben Forta 著 , 杨涛 译

  4. 正则表达式在线工具

相关推荐
吴声子夜歌9 小时前
正则表达式——边界
正则表达式
营养充电站16 小时前
C++23 新特性在 CLion 中的实战体验
macos·docker·jupyter·正则表达式
吴声子夜歌1 天前
正则表达式——字符组
正则表达式
天若有情6731 天前
自制浏览器端文本批量处理小工具,支持查找替换、内容删除、正则表达式,开箱即用
正则表达式
吴声子夜歌1 天前
正则表达式——简单的模式匹配
正则表达式
吴声子夜歌2 天前
正则表达式——前后查找
正则表达式
吴声子夜歌2 天前
正则表达式参考
正则表达式
吴声子夜歌2 天前
正则表达式——常见应用软件和编程语言中的正则表达式
正则表达式
吴声子夜歌3 天前
正则表达式——使用子表达式
正则表达式
吴声子夜歌3 天前
正则表达式——重复匹配
正则表达式