Fork me on Gitee

正则表达式

本文案例均来自于B站的《【韩顺平讲Java】Java 正则表达式专题 -正则 正则表达式 元字符 限定符 Pattern Matcher 分组 捕获 反向引用等》传送门

入门案例

import org.junit.Test;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

import static java.util.regex.Pattern.*;

/**
 * @author computer
 */
public class RegExpressionTest {

    /**
     * 验证:正则表达式体验 - 找到文本中所有的英文字母和数字
     */
    @Test
    public void test1() {
        String str = "1994年6月,在同约翰·盖吉、詹姆斯·高斯林、比尔·乔伊、帕特里克·诺顿、韦恩·罗斯因和埃里克·施密特经历了一场历时三天的头脑风暴后,团队决定再一次改变努力的目标,这次他们决定将该技术应用于万维网。他们认为随着Mosaic浏览器的到来,因特网正在向同样的高度互动的远景演变,而这一远景正是他们在有线电视网中看到的。作为原型,帕特里克·诺顿写了一个小型万维网浏览器,WebRunner,后来改名为HotJava[13]。\n" +
                "\n" +
                "1994年10月,HotJava和Java平台为公司高层进行演示。1994年,Java 1.0a版本已经可以提供下载,但是Java和HotJava浏览器的第一次公开发布却是在1995年3月23日SunWorld大会上进行的。升阳公司的科学指导约翰·盖吉宣告Java技术。这个发布是与网景公司的执行副总裁马克·安德森的惊人发布一起进行的,宣布网景将在其浏览器中包含对Java的支持。1996年1月,Sun公司成立了Java业务集团,专门开发Java技术。";
        Pattern pattern = compile("([A-Za-z0-9]+)");
        Matcher matcher = pattern.matcher(str);
        while (matcher.find()){
            System.out.println("找到" + matcher.group(0));
        }
    }

    /**
     * 验证:找到文本中所有的IP地址
     */
    @Test
    public void test2() {
        String str = "以下列出留用的内部私有地址\nA类 10.0.0.0--10.255.255.255\nB类 172.16.0.0--172.31.255.255\nC类 192.168.0.0--192.168.255.255";
        Pattern pattern = compile("\\d+.\\d+.\\d+.\\d+");
        Matcher matcher = pattern.matcher(str);
        while (matcher.find()){
            System.out.println("ip: " + matcher.group());
        }
    }
}

正则表达式底层实现

语法

字符匹配符

符号 说明 示例 解释 匹配输入
[ ] 可接收的字符列表 [abc] a、b、c中的任意1个字符
[^] 不接收的字符串列表 [^abc] 除a、b、c之外的任意1个字符,包括数字和特殊符号
- 连字符,表示范围 A-Z 任意A-Z之间的单个字母,等价于[ABC....Z]
. 匹配除\n (换行)以外的任何字符 a..b 以a开头,b结尾,中间包括2个任意字符的长度为4的字符串 abcb
\\d 匹配单个数字字符 == [0-9] \\d{3}(\\d)? 3个或4个数字的字符串 123、4847
\\D 对 \\d 取反 \\D(\\d)* 以单个非数字字符开头,后接任意个数字符串 m234
\\w 匹配单个数字、大小写字母字符、下划线 == [0-9a-zA-Z_] \\d{3}(\\w) 3个数字开头,后面为4个数字字母字符串
\\W 对 \\W 取反 \\W+\\d 至少1个非数字字母字符开头,2个数字结尾的字符串 #@~19
\\s 匹配任何空白字符(空格、制表符等)
\\S 对\\s取反
(?i)abc ,abc不区分大小写
a(?i)bc ,bc不区分大小写
a((?i)b)c ,b不区分大小写

总结

[]   表示可接受字符列表
[^]  表示不可接受字符列表
-  表示范围
.  表示除\n(换行符)之外的任意字符

==========以下为各种特殊情况的代替符号=============
[0123456789]	\\d 	数字集合
[^0123456789]	\\D 	非数字集合

[a-zA-Z0-9_] 	\\w 	常用数字字母集合
[^a-zA-Z0-9_]	\\W 	非常用数字字母集合

[ ] 	\\s 	空白字符(空格、制表符等)
[^ ] 	\\S 	非空白字符
a(?i)bc 	bc不区分大小写
a((?i)b)c 	b不区分大小写

代码示例

package com.xxx.verify.regularExpression;

import cn.hutool.core.util.StrUtil;
import org.junit.Test;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

import static java.util.regex.Pattern.*;

/**
 * @author computer
 */
public class RegExpressionTest {

/**
     * 验证:字符匹配符应用
     */
    @Test
    public void test4() {
        String str = "1aBc2b3AB2bc4@# fe";

        //基础用法
        verifyReg("[]匹配在列表中的任意一个字符",str,"[2b3]");
        verifyReg("[^]匹配不在列表中的任意一个字符",str,"[^@#]");
        verifyReg("-表示范围",str,"[a-z]");
        verifyReg(". 匹配除了\\n的任意一个字符 ",str,".");

        //简写匹配数字
        verifyReg("\\d匹配任意一个数字 = [0-9]",str,"\\d\\d");
        verifyReg("\\D匹配任意一个非数字 = [^0-9]",str,"\\D");

        //简写匹配常用字母数字
        verifyReg("\\w匹配常用字母数字 的集合中任意一个字符 = [a-zA-Z0-9_]",str,"\\w");
        verifyReg("\\W对上面取反 = [^a-zA-Z0-9_]",str,"\\W");

        //匹配任意空白字符(空格、制表符等)
        verifyReg("\\s匹配任意空白字符",str,"\\s");
        verifyReg("\\S匹配任意非空白字符",str,"\\S");

        //不区分字母大小写进行匹配
        verifyReg("(?i)abc表示abc不区分大小写",str,"(?i)abc");
        verifyReg("a(?i)bc表示bc不区分大小写",str,"a(?i)bc");
        verifyReg("a((?i)b)c表示b不区分大小写",str,"a((?i)b)c");

        //不区分大小写简略写法
        verifyReg("不区分大小写简略写法",str,"abc",true);
    }


    /**
     * 验证正则表达式
     *      传入内容和正则表达式,打印出匹配上的内容
     * @param content
     * @param reg
     */
    public void verifyReg(String target, String content,String reg,boolean isSensitive){
        Pattern pattern = isSensitive ? compile(reg, CASE_INSENSITIVE) : compile(reg);
        Matcher matcher = pattern.matcher(content);
        while (matcher.find()){
            if (StrUtil.isNotBlank(target)){
                System.out.println(target + " : " + matcher.group(0));
            }else {

                System.out.println(matcher.group(0));
            }
        }
    }

    public void verifyReg(String target, String content,String reg){
        verifyReg(target,content,reg,false);
    }
}

限定符

用于指定其前面的字符和组合项连续出现多少次

符号 说明 示例 解释 匹配输入
* 指定字符重复0次或n次(任意次数) [0,+∞] (abc)* 包含任意个abc的字符串
+ 指定字符重复1次或n次(至少一次) [1,+∞] m+(abc)* m开头,至少一次;后面接任意个abc mabc
? 指定字符重复0次或1次(最多一次) [0,1] m+abc? m开头,至少一次;后面ab/abc mmabc
只能输入n个字符 [abcd] 由abc组成的任意长度为3的字符串 abc/bcd/acd
指定至少n个匹配 [n,+∞] [abc] 由abc组成的任意长度不小于3的字符串
指定 [n,m) 范围内的字符数量 [abcd] 由abcd组成的任意长度[3,4]的字符串
/**
     * 验证:限定符
     */
    @Test
    public void test6() {
        //+指定字符出现[1,+∞]次
        verifyReg("+指定字符出现[1,+∞]次","abcaabb","a+");
        verifyReg("+指定字符出现[1,+∞]次","111111","\\d+");

        //?指定字符出现[0,1]次
        verifyReg("?指定字符出现[0,1]次","222ffe322d","a-z]+\\d?[");

        //*指定字符出现[0,+∞]次
        verifyReg("*指定字符出现[0,+∞]次","abcaabb","ab*");

        //{n}指定目标字符出现n次
        verifyReg("{n}表示指定字符出现次数","111111","1{2}");
        verifyReg("{n}表示指定字符出现次数","dabefacbghbcgac","[abc]{2}");


        //{n,m}指定目标字符出现[n,m)次,默认为贪婪匹配,即尽可能匹配多的
        verifyReg("{n,m}指定目标字符出现[n,m]次","aaa333aaaa2111","a{2,4}");
    }

选择匹配符

符号 说明 示例 解释 匹配输入
| 匹配"|"之前或之后的表达式 ab|cd ab或者cd
    /**
     * 验证:选择匹配符
     */
    @Test
    public void test5() {
        String str = "韩寒冷";
        verifyReg("|选择匹配任意",str,"韩|寒");
    }

定位符

规定要匹配的字符串出现的位置,比如在字符串的开始还是在结束的位置

符号 说明 示例 解释 匹配输入
^ 指定起始字符 ^[0-9]+[a-z]* 至少1个数字开头,后面接任意个小写字母 123、2fefe
$ 指定结束字符 ^\\d\\-[a-z]+$ 1个数字开头,连接一个-,最后以至少1个小写字母结尾 5-23b
\\b 匹配目标字符串的边界 han\\b 代码空格或者结束,即han后面为空格或者到了结尾 effhan nnfehan
\\B \\b的取反 han\\B 代表han后面不能由空格或者是结尾位置
/**
     * 验证:定位符
     */
    @Test
    public void test7() {
        //^指定起始字符
        verifyReg("^指定起始字符","12fef","^[0-9]+[a-z]*");

        //$指定结束字符
        verifyReg("^指定起始字符,$指定结束字符","1-fe","^\\d\\-[a-z]+$");

        //\\b匹配目标字符串的边界
        verifyReg("\\\\b匹配目标字符串的边界","efjahan nnfhanejihan","[a-z]?han\\b");

        //\\B匹配目标字符串的边界
        verifyReg("\\\\B对\\\\b的取反","efjahan nnfhanejihan","[a-z]?han\\B");
    }

分组

常用分组构造形式 说明
(pattern) 非命名捕获。捕获匹配的子字符串。编号为0的为第一个捕获是由整个正则表达式模式匹配的文本,其他捕获结果则根据左括号的顺序从1开始自动编号
(?<name>pattern) 命名捕获。将匹配的子字符串捕获到一个组名称或编号名称中。用于name的字符串不能包含任何标点符号,并且不能以数字开头。可以使用单引号代替尖括号,例如(?'name')
(?:pattern) 匹配pattern但不捕获该匹配的子表达式,即它是一个非捕获匹配,不存储供以后使用的匹配。对于用"or"字符(|)组合模式部件的情况很有用,例如: industr(?:y|ies) 是比 ‘industry|industries’更经济的表达式
(?=pattern) 非捕获匹配,例如 Windows(?=95|98|NT|2000) 匹配 "Windows2000"中的Windows,但是不匹配"Windows7"中的Windows
(?!pattern) 对(?=pattern)取反,Windows(?=95|98|NT|2000) 匹配 "Windows7"中的Windows,但是不匹配"Windows2000"中的Windows

代码示例

    /**
     * 验证:分组
     */
    @Test
    public void test8() {
        //(pattern) 非命名捕获,这里参考正则表达式原理章节
        //1. matcher.group(0) 得到匹配到的字符串
        //2. matcher.group(1) 得到匹配到的字符串的第1个分组内容
        //3. matcher.group(2) 得到匹配到的字符串的第2个分组内容
        Pattern pattern = compile("(\\d\\d)(\\d\\d)");
        Matcher matcher = pattern.matcher("3ff1256fe1909f");
        while (matcher.find()){
            System.out.println("非命名捕获 找到字符串:" + matcher.group(0));
            System.out.println("第1个分组内容: " + matcher.group(1));
            System.out.println("第2个分组内容: " + matcher.group(2));
        }

        //(?<name>pattern) 命名捕获
        pattern = compile("(?<g1>\\d\\d)(?<g2>\\d\\d)");
        matcher = pattern.matcher("3ff1256fe1909f");
        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"));
        }


        //(?:pattern) 非命名捕获,是选择匹配符|的替代写法,()中的表达式参与捕获
        //找到windows98或者windowsXP
        verifyReg("(?:pattern) 非命名捕获,()中的表达式参与捕获","windows98和windows2000和windowsXP和windows11","windows(?:98|XP)");
        verifyReg("(?:pattern) 另一种写法","windows98和windows2000和windowsXP和windows11","windows98|windowsXP");


        //(?=pattern) 非命名捕获,()中的表达式不参与捕获
        //找到结尾是98|XP的windows字符串
        verifyReg("(?=pattern) 非命名捕获,()中的表达式不参与捕获","windows98和windows2000和windowsXP和windows11","windows(?=98|XP)");

        //(?!pattern) 非命名捕获,()中的表达式不参与捕获,取反
        //找到结尾不是98|XP的windows字符串
        verifyReg("(?!pattern) 非命名捕获,()中的表达式不参与捕获","windows98和windows2000和windowsXP和windows11","windows(?!98)");


    }

贪婪匹配和非贪婪匹配

/**
     * 验证:贪婪匹配与非贪婪匹配
	 		限定符之后加上?,则只匹配最少的内容即停止
     */
    @Test
    public void test9() {
        verifyReg("贪婪匹配","fea11111","a\\d+");
        verifyReg("非贪婪匹配","fea11111","a\\d+?");
    }

正则实例(Pattern.matchers)

/**
     * 验证:应用实例
     */
    @Test
    public void test10() {
        //全部都是汉字
        String content = "中华人民共2和国";
        String reg = "^[\u4e00-\u9fa5]+$";
        verifyIsMatch("汉字",content,reg);

        //邮政编码: 1-9开头的一个六位数
        content = "362428";
        reg = "^[1-9]\\d{5}";
        verifyIsMatch("邮政编码",content,reg);

        //QQ号: 1-9开头的一个(5位数 - 10位数)
        content = "1060987218";
        reg = "^[1-9]\\d{4,9}";
        verifyIsMatch("QQ号",content,reg);

        //手机号: 13、14、15、16、18、19开头的一个11位数)
        content = "18170461186";
        reg = "^1[3|4|5|6|8|9]\\d{9}";
        verifyIsMatch("手机号",content,reg);

        //URL
        content = "https://www.bilibili.com/video/BV1Eq4y1E79W?p=17&spm_id_from=pageDriver";
        //1. ^((http|https)://)     匹配 http:// 或者 https://
        //2. ([\w-]+\.)+[\w-]+      匹配 www.bilibili.com
        //3. (/[\w-?=&./%#]*)?       匹配 /video/BV1Eq4y1E79W?p=17&spm_id_from=pageDriver
        reg = "^((http|https)://)?([\\w-]+\\.)+[\\w-]+(/[\\w-?=&./%#]*)?$";
        verifyIsMatch("URL",content,reg);

        //电子邮件:1.只能有一个@ 2. @前面是用户名 a-zA-Z0-9 3. @后面是域名,只能是英文字母,例如sohu.com tinghua.com.cn
        content = "102893488@qq.com";
        reg = "^[\\w-]+@([a-zA-Z]+\\.)+[a-zA-Z]+$";
        verifyIsMatch("邮箱",content,reg);

        //整数或者小数,考虑正负
        content = "0.2";
        reg = "^[+-]?([1-9]+\\d*|0)(\\.\\d+)?$";
        verifyIsMatch("整数或者小数",content,reg);

        //对URL进行解析,得到1. 协议; 2. 域名; 3. 端口; 4. 资源
        content = "http://www.souhu.com:8089/abc/index.html";
        reg = "^([a-zA-Z]+)://([a-zA-Z.]+[\\w-]+):(\\d+)[\\w-/]*/([\\w.]+)$";
        verifyIsMatch("url",content,reg);
        Pattern pattern = compile(reg);
        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));
        }
    }

Matcher类的常用方法

方法列表

方法 说明
matcher.start() 获取当前匹配到的内容的起始下标
matcher.end() 获取当前匹配到的内容的结束下标
matcher.matchers() 整体匹配,用于验证字符串是否某个规则
matcher.replaceAll() 将匹配中的字符串进行全部替换

代码示例

    /**
     * 验证:Matcher方法
     */
    @Test
    public void test11() {
        String content = "hello abc hello tom hello cat";
        String reg = "hello";
        Pattern pattern = compile(reg);
        Matcher matcher = pattern.matcher(content);
        //matcher.start() 获取当前匹配到的内容的起始下标
        //matcher.end()   获取当前匹配到的内容的结束下标
        while (matcher.find()){
            System.out.println("==========");
            System.out.println(matcher.start());
            System.out.println(matcher.end());
            System.out.println("group方法获取: "+matcher.group(0));
            System.out.println("通过位置获取" + content.substring(matcher.start(),matcher.end()));
        }

        //matcher.matches()
        //整体匹配,用于验证字符串是否某个规则
        pattern = compile("[1-9]\\d{5}");
        matcher = pattern.matcher("362428");
        System.out.println(matcher.matches() ? "邮政编码格式: YES" : "邮政编码格式: NO");

        //matcher.replaceAll()
        //将匹配中的字符串进行全部替换
        pattern = compile("\\(\\s*?\\S*?\\)");
        matcher = pattern.matcher("JAVA是一门动态(即时编译)编程语言(language)");
        System.out.println(matcher.replaceAll(""));
    }

反向引用

代码示例

    /**
     * 验证:反向引用
     *      圆括号的内容被捕获后,可以在这个括号后被使用,我们称为反向引用;这种引用在正则表达式内外部都可以
     *      内部反向引用 \\分组号
     *      外部反向引用 $分组号、
     */
    @Test
    public void test12() {
        verifyReg("匹配两个连续的相同数字","213433fe442fjie2133","(\\d)\\1");
        verifyReg("匹配五个连续的相同数字","333333211124455443433333222222","(\\d)\\1{4}");
        verifyReg("匹配个位与千位相同,十位与百位相同的数字,即ABBA","A3443B5445C3342","(\\d)(\\d)\\2\\1");
        verifyReg("前面5位数字,中间-,最后9位数字,每3位相同","fe212132323-333222999fe3d23225","\\d{5}-(\\d)\\1{2}(\\d)\\2{2}(\\d)\\3{2}");

        //我...我要...学学学学...编程
        String content = "我...我要...学学学学...编程";
        Pattern pattern = compile("\\.");
        Matcher matcher = pattern.matcher(content);
        content = matcher.replaceAll("");
        String $1 = compile("(.)\\1+").matcher(content).replaceAll("$1");
        System.out.println($1);
    }

String类中直接使用正则表达式

代码示例

    /**
     * 验证:String类直接支持正则表达式
     */
    @Test
    public void test13() {
        //替换 replaceAll
        System.out.println("jdk1.3和jdk1.4发布于98年,其中jdk1.3".replaceAll("jdk1\\.(3|4)", "JDK"));
        //匹配 matches
        System.out.println(("18176453321".matches("1(3|5|7|8|9)\\d{9}")) ? "手机号码格式正确" : "格式不正确");
        //分割 split
        String[] split = "hello#fjei-ji2和~河南".split("\\#|\\-|\\~");
        for (String s : split) {
            System.out.println(s);
        }
    }

完整代码

package com.pxl.verify.regularExpression;

import cn.hutool.core.util.StrUtil;
import org.junit.Test;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

import static java.util.regex.Pattern.*;

/**
 * @author computer
 */
public class RegExpressionTest {

    /**
     * 验证:正则表达式体验 - 找到文本中所有的英文字母和数字
     */
    @Test
    public void test1() {
        String str = "1994年6月,在同约翰·盖吉、詹姆斯·高斯林、比尔·乔伊、帕特里克·诺顿、韦恩·罗斯因和埃里克·施密特经历了一场历时三天的头脑风暴后,团队决定再一次改变努力的目标,这次他们决定将该技术应用于万维网。他们认为随着Mosaic浏览器的到来,因特网正在向同样的高度互动的远景演变,而这一远景正是他们在有线电视网中看到的。作为原型,帕特里克·诺顿写了一个小型万维网浏览器,WebRunner,后来改名为HotJava[13]。\n" +
                "\n" +
                "1994年10月,HotJava和Java平台为公司高层进行演示。1994年,Java 1.0a版本已经可以提供下载,但是Java和HotJava浏览器的第一次公开发布却是在1995年3月23日SunWorld大会上进行的。升阳公司的科学指导约翰·盖吉宣告Java技术。这个发布是与网景公司的执行副总裁马克·安德森的惊人发布一起进行的,宣布网景将在其浏览器中包含对Java的支持。1996年1月,Sun公司成立了Java业务集团,专门开发Java技术。";
        Pattern pattern = compile("([A-Za-z0-9]+)");
        Matcher matcher = pattern.matcher(str);
        while (matcher.find()){
            System.out.println("找到" + matcher.group(0));
        }
    }

    /**
     * 验证:找到文本中所有的IP地址
     */
    @Test
    public void test2() {
        String str = "以下列出留用的内部私有地址\nA类 10.0.0.0--10.255.255.255\nB类 172.16.0.0--172.31.255.255\nC类 192.168.0.0--192.168.255.255";
        Pattern pattern = compile("\\d+.\\d+.\\d+.\\d+");
        Matcher matcher = pattern.matcher(str);
        while (matcher.find()){
            System.out.println("ip: " + matcher.group());
        }
    }


    /**
     * 验证:正则表达式的转义符
     *   java中使用两个反斜线对特殊字符(.*+()$/\?[]^{})进行转义
     */
    @Test
    public void test3() {
        String str = "abc$(cdf$(";
        Pattern pattern = compile("\\$");
        Matcher matcher = pattern.matcher(str);
        while (matcher.find()){
            System.out.println(matcher.group(0));
        }
    }

    /**
     * 验证:字符匹配符应用
     */
    @Test
    public void test4() {
        String str = "1aBc2b3AB-2_bc4@# fe";

        //基础用法
        verifyReg("[]匹配在列表中的任意一个字符",str,"[2b3]");
        verifyReg("[^]匹配不在列表中的任意一个字符",str,"[^@#]");
        verifyReg("-表示范围",str,"[a-z]");
        verifyReg(". 匹配除了\\n的任意一个字符 ",str,".");

        //简写匹配数字
        verifyReg("\\d匹配任意一个数字 = [0-9]",str,"\\d\\d");
        verifyReg("\\D匹配任意一个非数字 = [^0-9]",str,"\\D");

        //简写匹配常用字母数字
        verifyReg("\\w匹配常用字母数字 的集合中任意一个字符 = [a-zA-Z0-9_]",str,"\\w");
        verifyReg("\\W对上面取反 = [^a-zA-Z0-9_]",str,"\\W");

        //匹配任意空白字符(空格、制表符等)
        verifyReg("\\s匹配任意空白字符",str,"\\s");
        verifyReg("\\S匹配任意非空白字符",str,"\\S");

        //不区分字母大小写进行匹配
        verifyReg("(?i)abc表示abc不区分大小写",str,"(?i)abc");
        verifyReg("a(?i)bc表示bc不区分大小写",str,"a(?i)bc");
        verifyReg("a((?i)b)c表示b不区分大小写",str,"a((?i)b)c");

        //不区分大小写简略写法
        verifyReg("不区分大小写简略写法",str,"abc",true);
    }


    /**
     * 验证:选择匹配符
     */
    @Test
    public void test5() {
        String str = "韩寒冷";
        verifyReg("|选择匹配",str,"韩|寒");
    }

    /**
     * 验证:限定符
     */
    @Test
    public void test6() {
        //+指定字符出现[1,+∞]次
        verifyReg("+指定字符出现[1,+∞]次","abcaabb","a+");
        verifyReg("+指定字符出现[1,+∞]次","111111","\\d+");

        //?指定字符出现[0,1]次
        verifyReg("?指定字符出现[0,1]次","222ffe322d","a-z]+\\d?[");

        //*指定字符出现[0,+∞]次
        verifyReg("*指定字符出现[0,+∞]次","abcaabb","ab*");

        //{n}指定目标字符出现n次
        verifyReg("{n}表示指定字符出现次数","111111","1{2}");
        verifyReg("{n}表示指定字符出现次数","dabefacbghbcgac","[abc]{2}");


        //{n,m}指定目标字符出现[n,m)次,默认为贪婪匹配,即尽可能匹配多的
        verifyReg("{n,m}指定目标字符出现[n,m]次","aaa333aaaa2111","a{2,4}");
    }


    /**
     * 验证:定位符
     */
    @Test
    public void test7() {
        //^指定起始字符
        verifyReg("^指定起始字符","12fef","^[0-9]+[a-z]*");

        //$指定结束字符
        verifyReg("^指定起始字符,$指定结束字符","1-fe","^\\d\\-[a-z]+$");

        //\\b匹配目标字符串的边界
        verifyReg("\\\\b匹配目标字符串的边界","efjahan nnfhanejihan","[a-z]?han\\b");

        //\\B匹配目标字符串的边界
        verifyReg("\\\\B对\\\\b的取反","efjahan nnfhanejihan","[a-z]?han\\B");
    }


    /**
     * 验证:分组
     */
    @Test
    public void test8() {
        //(pattern) 非命名捕获,这里参考正则表达式原理章节
        //1. matcher.group(0) 得到匹配到的字符串
        //2. matcher.group(1) 得到匹配到的字符串的第1个分组内容
        //3. matcher.group(2) 得到匹配到的字符串的第2个分组内容
        Pattern pattern = compile("(\\d\\d)(\\d\\d)");
        Matcher matcher = pattern.matcher("3ff1256fe1909f");
        while (matcher.find()){
            System.out.println("非命名捕获 找到字符串:" + matcher.group(0));
            System.out.println("第1个分组内容: " + matcher.group(1));
            System.out.println("第2个分组内容: " + matcher.group(2));
        }

        //(?<name>pattern) 命名捕获
        pattern = compile("(?<g1>\\d\\d)(?<g2>\\d\\d)");
        matcher = pattern.matcher("3ff1256fe1909f");
        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"));
        }


        //(?:pattern) 非命名捕获,是选择匹配符|的替代写法,()中的表达式参与捕获
        //找到windows98或者windowsXP
        verifyReg("(?:pattern) 非命名捕获,()中的表达式参与捕获","windows98和windows2000和windowsXP和windows11","windows(?:98|XP)");
        verifyReg("(?:pattern) 另一种写法","windows98和windows2000和windowsXP和windows11","windows98|windowsXP");


        //(?=pattern) 非命名捕获,()中的表达式不参与捕获
        //找到结尾是98|XP的windows字符串
        verifyReg("(?=pattern) 非命名捕获,()中的表达式不参与捕获","windows98和windows2000和windowsXP和windows11","windows(?=98|XP)");

        //(?!pattern) 非命名捕获,()中的表达式不参与捕获,取反
        //找到结尾不是98|XP的windows字符串
        verifyReg("(?!pattern) 非命名捕获,()中的表达式不参与捕获","windows98和windows2000和windowsXP和windows11","windows(?!98)");
    }


    /**
     * 验证:贪婪匹配与非贪婪匹配
     */
    @Test
    public void test9() {
        verifyReg("贪婪匹配","fea11111","a\\d+");
        verifyReg("非贪婪匹配","fea11111","a\\d+?");
    }

    /**
     * 验证:应用实例
     */
    @Test
    public void test10() {
        //全部都是汉字
        String content = "中华人民共2和国";
        String reg = "^[\u4e00-\u9fa5]+$";
        verifyIsMatch("汉字",content,reg);

        //邮政编码: 1-9开头的一个六位数
        content = "362428";
        reg = "^[1-9]\\d{5}";
        verifyIsMatch("邮政编码",content,reg);

        //QQ号: 1-9开头的一个(5位数 - 10位数)
        content = "1060987218";
        reg = "^[1-9]\\d{4,9}";
        verifyIsMatch("QQ号",content,reg);

        //手机号: 13、14、15、16、18、19开头的一个11位数)
        content = "18170461186";
        reg = "^1[3|4|5|6|8|9]\\d{9}";
        verifyIsMatch("手机号",content,reg);

        //URL
        content = "https://www.bilibili.com/video/BV1Eq4y1E79W?p=17&spm_id_from=pageDriver";
        //1. ^((http|https)://)     匹配 http:// 或者 https://
        //2. ([\w-]+\.)+[\w-]+      匹配 www.bilibili.com
        //3. (/[\w-?=&./%#]*)?       匹配 /video/BV1Eq4y1E79W?p=17&spm_id_from=pageDriver
        reg = "^((http|https)://)?([\\w-]+\\.)+[\\w-]+(/[\\w-?=&./%#]*)?$";
        verifyIsMatch("URL",content,reg);

        //电子邮件:1.只能有一个@ 2. @前面是用户名 a-zA-Z0-9 3. @后面是域名,只能是英文字母,例如sohu.com tinghua.com.cn
        content = "102893488@qq.com";
        reg = "^[\\w-]+@([a-zA-Z]+\\.)+[a-zA-Z]+$";
        verifyIsMatch("邮箱",content,reg);

        //整数或者小数,考虑正负
        content = "0.2";
        reg = "^[+-]?([1-9]+\\d*|0)(\\.\\d+)?$";
        verifyIsMatch("整数或者小数",content,reg);

        //对URL进行解析,得到1. 协议; 2. 域名; 3. 端口; 4. 资源
        content = "http://www.souhu.com:8089/abc/index.html";
        reg = "^([a-zA-Z]+)://([a-zA-Z.]+[\\w-]+):(\\d+)[\\w-/]*/([\\w.]+)$";
        verifyIsMatch("url",content,reg);
        Pattern pattern = compile(reg);
        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));
        }
    }


    /**
     * 验证:Matcher方法
     */
    @Test
    public void test11() {
        String content = "hello abc hello tom hello cat";
        String reg = "hello";
        Pattern pattern = compile(reg);
        Matcher matcher = pattern.matcher(content);
        //matcher.start() 获取当前匹配到的内容的起始下标
        //matcher.end()   获取当前匹配到的内容的结束下标
        while (matcher.find()){
            System.out.println("==========");
            System.out.println(matcher.start());
            System.out.println(matcher.end());
            System.out.println("group方法获取: "+matcher.group(0));
            System.out.println("通过位置获取" + content.substring(matcher.start(),matcher.end()));
        }

        //matcher.matches()
        //整体匹配,用于验证字符串是否某个规则
        pattern = compile("[1-9]\\d{5}");
        matcher = pattern.matcher("362428");
        System.out.println(matcher.matches() ? "邮政编码格式: YES" : "邮政编码格式: NO");

        //matcher.replaceAll()
        //将匹配中的字符串进行全部替换
        pattern = compile("\\(\\s*?\\S*?\\)");
        matcher = pattern.matcher("JAVA是一门动态(即时编译)编程语言(language)");
        System.out.println(matcher.replaceAll(""));
    }


    /**
     * 验证:反向引用
     *      圆括号的内容被捕获后,可以在这个括号后被使用,我们称为反向引用;这种引用在正则表达式内外部都可以
     *      内部反向引用 \\分组号
     *      外部反向引用 $分组号、
     */
    @Test
    public void test12() {
        verifyReg("匹配两个连续的相同数字","213433fe442fjie2133","(\\d)\\1");
        verifyReg("匹配五个连续的相同数字","333333211124455443433333222222","(\\d)\\1{4}");
        verifyReg("匹配个位与千位相同,十位与百位相同的数字,即ABBA","A3443B5445C3342","(\\d)(\\d)\\2\\1");
        verifyReg("前面5位数字,中间-,最后9位数字,每3位相同","fe212132323-333222999fe3d23225","\\d{5}-(\\d)\\1{2}(\\d)\\2{2}(\\d)\\3{2}");

        //我...我要...学学学学...编程
        String content = "我...我要...学学学学...编程";
        Pattern pattern = compile("\\.");
        Matcher matcher = pattern.matcher(content);
        content = matcher.replaceAll("");
        String $1 = compile("(.)\\1+").matcher(content).replaceAll("$1");
        System.out.println($1);
    }

    /**
     * 验证:String类直接支持正则表达式
     */
    @Test
    public void test13() {
        //替换 replaceAll
        System.out.println("jdk1.3和jdk1.4发布于98年,其中jdk1.3".replaceAll("jdk1\\.(3|4)", "JDK"));
        //匹配 matches
        System.out.println(("18176453321".matches("1(3|5|7|8|9)\\d{9}")) ? "手机号码格式正确" : "格式不正确");
        //分割 split
        String[] split = "hello#fjei-ji2和~河南".split("\\#|\\-|\\~");
        for (String s : split) {
            System.out.println(s);
        }
    }

    public void verifyIsMatch(String target,String content,String reg){
        System.out.println(Pattern.matches(reg,content) ? target +",整体匹配成功" : target +",整体匹配失败");
    }

    /**
     * 验证正则表达式
     *      传入内容和正则表达式,打印出匹配上的内容
     * @param content
     * @param reg
     */
    public void verifyReg(String target, String content,String reg,boolean isSensitive){
        Pattern pattern = isSensitive ? compile(reg, CASE_INSENSITIVE) : compile(reg);
        Matcher matcher = pattern.matcher(content);
        while (matcher.find()){
            if (StrUtil.isNotBlank(target)){
                System.out.println(target + " : " + matcher.group(0));
            }else {

                System.out.println(matcher.group(0));
            }
        }
    }

    public void verifyReg(String target, String content,String reg){
        verifyReg(target,content,reg,false);
    }
}
posted @ 2021-12-27 23:38  江南西道  阅读(78)  评论(0编辑  收藏  举报
Fork me on GitHub