正则表达式

文章目录

  • 1 正则表达式基本介绍
  • 2 正则表达式语法
    • 2.1 元字符-转义号 \\\
    • 2.2 元字符-字符匹配符
    • 2.3 元字符-选择匹配符
    • 2.4 元字符-限定符
    • 2.5 元字符-定位符
    • 2.6 分组
  • 3 正则表达式三个常用类
    • 3.1 Pattern类的Matches方法
    • 3.2 Matcher类的常用方法
  • 4 分组、捕获、反向引用
  • 5 String类中使用正则表达式
    • 5.1 替换功能
    • 5.2 判断功能
    • 5.3 分割功能
  • 6 正则表达式使用案例
    • 6.1 验证电子邮箱格式是否合法
    • 6.2 验证手机号码是否合法
    • 6.3 验证URL格式是否合法
  • 7 正则表达式底层实现
  • 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“
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 = "(?\\d\\d)(?\\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方法

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类的常用方法

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)

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 验证电子邮箱格式是否合法

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 = "[email protected]";
        String content = "[email protected]";
        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

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格式是否合法

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 正则表达式底层实现

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. 正则表达式在线工具

你可能感兴趣的:(Java,正则表达式)