Java正则表达式

正则表达式

正则表达式语法

转义字符

  • . * + ( ) $ / \ ? [ ] ^ { } - | ? 等
  • 上述元字符在正则表达式中使用需要用到\\

字符匹配符

符号含义示例解释
[ ]可接收的字符列表[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|cdab或者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("匹配失败!");
        }
    }
}

正则表达式善于处理文本,对匹配、搜索和替换等操作都有意想不到的作用。正因如此,正则表达式现在是作为程序员七种基本技能之一*,因此学习和使用它在工作中都能达到很高的效率。 正则表达式应用于程序设计语言中,首次是出现在 Perl 语言,这也让 Perl 奠定了正则表达式旗手的地位。现在,它已经深入到了所有的程序设计语言中,在程序设计语言中,正则表达式可以说是标准配置了。 Java 中从 JDK 1.4 开始增加了对正则表达式的支持,至此正则表达式成为了 Java 中的基本类库,使用时不需要再导入第三方的类库了。Java 正则表达式的语法来源于象征着正则表达式标准的 Perl 语言,但也不是完全相同的,具体的可以参看 Pattern 类的 API 文档说明。 我在一次偶然中发现了位于 java.sun.com 站点上的 Java Tutorial,也在那里看到了关于 Java正则表达式教程,感觉它不同于其他的正则表达式教程,文中以大量的匹配实例来进行说明。为了能让 Java 学习者能更好地使用正则表达式,就将其完整地译出了。该教程中所介绍的正则表达式应用仅仅是最为简单的(并没有完全地涉及到 Pattern 类支持的所有正则表达式语法,也没有涉及到高级的应用),适合于从未接触过或者是尚未完全明白正则表达式基础的学习者。在学习完该教程后,应该对正则表达式有了初步的了解,并能熟练地运用 java.util.regex 包中的关于正则表达式的类库,为今后学习更高级的正则表达式技术奠定良好的基础。 教程中所有的源代码都在 src 目录下,可以直接编译运行。由于当前版本的 Java Tutorial 是基于 JDK 6.0 的,因此其中的示例程序也用到了 JDK 6.0 中的新增类库,但正则表达式在 JDK 1.4 就已经存在了,为了方便大家使用,改写了部分的源代码,源代码类名中后缀为"V4"的表示用于 JDK 1.4 或以上版本,"V5"的表示用于 JDK 5.0 或以上版本,没有这些后缀的类在各个版本中均可以正常使用。 由于译者的水平和技术能力有限,译稿虽经多次校对,难免有疏漏之处,敬请大家批评和指正。若有发现不妥之处,请发送邮件至 FrankieGao123@gmail.com,我会在 blog 中进行勘误,谢谢!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

请多多指教呀

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值