重学JavaSE 第20章 : 正则表达式介绍、String类相关使用、正则表达式速查表


正则表达式

MJ: Java正则表达式

为什么要学习正则表达式

  • 提取文章中所有的 英文单词 / 数字 / 或者单词数字
    如果按照我们对字符串的操作满足上面的要求, 工作量会很大; 这时我们就需要使用 正则表达式

  • 当我们对字符串或文本进行过滤/提取, 就需要使用正则表达式; 正则表达式是处理文本的利器

  • 为了解决上面的问题, Java提供了正则表达式技术, 专门用于处理类似的问题; 简单的说: 正则表达式是对字符串执行模式匹配的技术

  • 正则表达式 — regular expression — RegExp

正则表达式介绍

正则表达式是对字符串执行模式匹配的技术

一个正则表达式, 就是用某种模式去匹配字符串的一个公式; 一旦掌握正则表达式, 之前花费几小时而且容易出错的文本处理工作缩短在几分钟内完成。

正则初体验(先感受一波)

public class Regexp01 {

    @Test
    public void test01() {
	
	// 正则表达式, 模式匹配, 根据提供的文本内容, 一个字符一个字符进行模式匹配

        String content = "1998年12月8日,第二代Java平台的企业版J2EE发布。";
        //1. 先创建一个Pattern对象, 模式对象 --> 正则表达式对象
        
        // ----提取文章中所有的英文单词----
        // Pattern pattern = Pattern.compile("[a-zA-Z]+");
        
        // ----提取文章中所有的数字----
		// Pattern pattern = Pattern.compile("[0-9]+");  
		
		// ----提取文章中所有的英文单词和数字----               
		Pattern pattern = Pattern.compile("([0-9]+)|([a-zA-Z]+)"); 

        //2. 创建匹配器对象;
        Matcher matcher = pattern.matcher(content);
        //3. 开始循环匹配. 找到就返回true, 否则返回false
        while (matcher.find()) {
            // 匹配内容,文本,放到m.group(0)中
            System.out.println("找到:" + matcher.group(0));
        }
    }
    找到:1998
	找到:12
	找到:8
	找到:Java
	找到:J
	找到:2
	找到:EE

    @Test
    public void test02() {
        String content = "<a target=\"_blank\" title=\"国家卫健委:疫苗接种遵循自愿原则\" href=\"/s?wd=%E5%9B%BD%E5%AE%B6%E5%8D%AB%E5%81%A5%E5%A7%94%3A%E7%96%AB%E8%8B%97%E6%8E%A5%E7%A7%8D%E9%81%B5%E5%BE%AA%E8%87%AA%E6%84%BF%E5%8E%9F%E5%88%99&rsv_idx=2&tn=baiduhome_pg&usm=1&ie=utf-8&rsv_cq=%E7%A7%91%E7%8F%AD%E5%87%BA%E8%BA%AB&rsv_dl=0_right_fyb_pchot_20811_01&rsv_pq=dbab8cc20003ffb0&oq=%E7%A7%91%E7%8F%AD%E5%87%BA%E8%BA%AB&rsv_t=b28e0Dh2RwijSbY%2FN0sKfuoVtpXIHJ0kuYUb%2Fv6j2nsD2WjcXm99mlcfIKnEFzgFNbjc&rsf=139c9f7a210ba310fe965fe0afd98c58_1_10_1\" class=\"c-font-medium c-color-t opr-toplist1-subtitle\">\n" +
                "                国家卫健委:疫苗接种遵循自愿原则\n" +
                "                </a>";

        //1. 先创建一个Pattern对象, 模式对象 --> 正则表达式对象
        Pattern pattern = Pattern.compile("<a target=\"_blank\" title=\"(\\S*)\"");
        //2. 创建匹配器对象;
        Matcher matcher = pattern.matcher(content);
        //3. 开始循环匹配. 找到就返回true, 否则返回false
        while (matcher.find()) {
            System.out.println("找到:" + matcher.group(1));
        }
    }
    找到:国家卫健委:疫苗接种遵循自愿原则

    @Test
    public void test03() {
        String content = "私有地址(Private address)属于非注册地址,专门为组织机构内部使用。\n" +
                "以下列出留用的内部私有地址\n" +
                "A类 10.0.0.0--10.255.255.255\n" +
                "B类 172.16.0.0--172.31.255.255\n" +
                "C类 192.168.0.0--192.168.255.255";

        //1. 先创建一个Pattern对象, 模式对象 --> 正则表达式对象
        Pattern pattern = Pattern.compile("\\d+\\.\\d+\\.\\d+\\.\\d+");
        //2. 创建匹配器对象;
        Matcher matcher = pattern.matcher(content);
        //3. 开始循环匹配. 找到就返回true, 否则返回false
        while (matcher.find()) {
            System.out.println("找到:" + matcher.group(0));
        }
    }
    找到:10.0.0.0
	找到:10.255.255.255
	找到:172.16.0.0
	找到:172.31.255.255
	找到:192.168.0.0
	找到:192.168.255.255
}

正则表达式底层原理

  • 为让大家对正则表达式底层实现有一个直观的映象,给大家举个实例给你一段字符串(文本),请找出所有四个数字连在一起的子串,比如:应该找到 1998 1999 3443 9889
/**
 * Description: 分析Java的正则表达式的底层实现(重要)
 *
 * @date 2022/3/27 09:22
 */
public class Regexp02 {

    @Test
    public void test01() {

        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平台的标准版),应用3443于桌面环境;J2EE(Java 2Enterprise Edition,Java 2平台的企5775业版),应用于1568基于Java的应用服务器。Java 2平台的发布,是Java发展过程中最重要的一个里程碑,标志着Java的应用开始普及6886。";

        // 目标:匹配所有四个数字
        // 说明
        // 1.\\d代表任意一个数字
        String regStr = "(\\d\\d)(\\d\\d)";
        // 2.创建模式对象(及正则表达式)
        Pattern pattern = Pattern.compile(regStr);
        // 3.创建匹配器
        // 说明:创建匹配器matcher,按照  正则表达式的规律   去匹配  content字符串
        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的值即为4,即下次执行find时, 就从4开始匹配
        *
        * matcher.group(0) 源码分析
          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();
           }
        * 1.根据groups[0]=0 和 groups[1] = 4  的记录的位置,从content开始截取子字符串返回
        *   就是[0,4)  包含0 但是不包含索引为4的位置
        *
        * 1.根据groups[0]=31 和 groups[1] = 35  的记录的位置,从content开始截取子字符串返回
        *   就是[31,35)  包含0 但是不包含索引为35的位置
        *
        * 如果再次指向find方法,仍然按上面分析来执行
        * */
        while (matcher.find()) {
            // 小结
            // 1.如果正则表达式有() 即分组
            // 2.取出匹配的字符串规则如下
            // 3.group(0) 表示匹配到的子字符串
            // 4.group(1) 表示匹配到的子字符串的第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));
        }
    }
}

找到:19981()匹配到的值=192()匹配到的值=98
找到:19991()匹配到的值=192()匹配到的值=99
找到:34431()匹配到的值=342()匹配到的值=43
找到:57751()匹配到的值=572()匹配到的值=75
找到:15681()匹配到的值=152()匹配到的值=68
找到:68861()匹配到的值=682()匹配到的值=86

在这里插入图片描述

正则表达式语法

如果想灵活的运用正则表达式,必须了解其中各种元字符的功能,元字符从功能上大致分为:

  • 限定符 (量词—>限制数量)
  • 字符匹配符 (单字匹配符)
  • 定位符 (边界匹配符)
  • 选择匹配符 ( | )
  • 分组组合和反向引用符
  • 特殊字符

在我们使用正则表达式去检索某些特殊字符的时候,需要用到转义符号\\,否则检索不到结果; 需要转义的字符: ( [ { / ^ - $ ¦ } ] ) ? * + .

*表示0个或多个 ?表示0个或1个 +表示1个或n个

注意: 下面通过: 定义content, regStr, pattern, matcher, matcher.find()等; 不是完全匹配; 而是判断并输出content中是否有符合regStr规则的子串; content中的内容是按字符从左到右匹配

  • 后面我们会学到matches()方法, 返回类型为boolean, 该方法为完全匹配。
// 写的正则表达式regStr是否匹配content中符合规则的子串, 并返回子串
public class Regexp03 {
	// 转义字符测试
    @Test
    public void test01() {
        String content = "A1234abc$(a.bc(a12b323(";
        //String regStr = "\\(";
        //String regStr = "\\."; // 等同于[.]
        //String regStr = "\\d{3}"; // 匹配连续3个数字的子串,相当于\\d\\d\\d // 123 323
        //String regStr = "a..b"; // a12b 注意: .可以匹配任何非\n的字符
        //String regStr = "\\d{3}(\\d)?"; // ?表示0/1, 该表达式意思为匹配连续3个或4个的字符数字
        //String regStr = "\\D(\\d)*"; // 以单个非数字字符开头,后接任意个数字字符串; *表示0个或多个 ?表示0个或1个 +表示1个或n个
        //String regStr = "[.]"; // 根据.来匹配, 在[]中的. ?都不需要转义
        
        //String content = "a";
        //String regStr = "a?"; //结果: "a" [0,1) 和 "" [1, 1) 可以匹配一个空串,因为空串也符合0次; ? 表示0次或1次
        
        String regStr = "[bc]{2}"; // 相当于[bc][bc], [bc]是单字符匹配符,[bc]一次只能出现b或c; 所以匹配的内容可以是bb bc cc
        Pattern pattern = Pattern.compile(regStr);
        Matcher matcher = pattern.matcher(content);
        while (matcher.find()) {
            System.out.println("找到: " + matcher.group(0));
        }
    }
    找到: bc
	找到: bc
}

元字符 — 单字符匹配符 (只能匹配一个字符)

在这里插入图片描述

预定义字符: 预先定义好的
在这里插入图片描述

public class Regexp04 {

    @Test
    public void test01() {
        String content = "@a11c8a bcABCy_ ";
        //String regStr = "[a-z]";      // 匹配a-z中的任意字符
        //String regStr = "[A-Z]";      // 匹配A-Z中的任意字符
        //String regStr = "abc";        // 匹配abc子串
        //String regStr = "(?i)abc";    // 匹配abc/ABC子串,大小写不敏感; a(?i)bc表示bc不区分大小写; a((?i)b)c表示b不区分大小写
        //String regStr = "[0-9]";      // 匹配0到9之间的数字
        //String regStr = "[^a-z]";     // 匹配非a-z中的字符, 1 8
        //String regStr = "[^a-z]{2}";  // 匹配非a-z中的字符,且为连续的2个字符; 11
        //String regStr = "[^0-9]{3}";  // 匹配非0-9的字符,且连续3个字符; abc ABC
        //String regStr = "[^abcd]";    // 匹配非abcd中字符的
        //String regStr = "\\D{3}";     // \\D和[^0-9]等价; \\d和[0-9]等价
        //String regStr = "\\w";        // 匹配 字母,数字,下划线 等价 [0-9a-zA-Z_]
        //String regStr = "\\W";        // 匹配非 字母,数字,下划线 等价 [^0-9a-zA-Z_]
        //String regStr = "\\s";        // 匹配任何空白字符
        //String regStr = "\\S";        // 匹配任何非空白字符
        String regStr = ".";            // 匹配出\n之外的所有字符, 如果要匹配. 本身要使用转义\\.
        //Pattern pattern = Pattern.compile(regStr, Pattern.CASE_INSENSITIVE); // Pattern.CASE_INSENSITIVE也表示对大小写不敏感
        Pattern pattern = Pattern.compile(regStr);
        Matcher matcher = pattern.matcher(content);
        while (matcher.find()) {
            System.out.println("找到: " + matcher.group(0));
        }
    }
}

元字符 — 选择匹配符

在这里插入图片描述

[abc]等价于 [a|b|c]等价于(a|b|c)

@Test
public void test() {
    String content = "zhangsanfeng 张 章";
    String regStr = "zhang|张|章";
    Pattern pattern = Pattern.compile(regStr);
    Matcher matcher = pattern.matcher(content);
    while (matcher.find()) {
        System.out.println("找到: " + matcher.group(0));
    }
}

找到: zhang
找到: 张
找到:

元字符 — 限定符 (量词)

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

在这里插入图片描述

public class Regexp05 {
    @Test
    public void test02() {
        String content = "1111111aaaahello";
        //String regStr = "a{3}";   // 等价aaa, 匹配aaa
        //String regStr = "1{4}";   // 表示匹配1111
        //String regStr = "\\d{2}"; // 表示出现连续2位的数字, 11 11 11
        //String regStr = "a{3,4}"; // 表示匹配aaa或aaaa; 细节: java匹配默认是贪婪匹配, 尽可能匹配多的 aaaa
        //String regStr = "1{4,5}"; // 11111
        String regStr = "\\d{2,5}"; // 表示匹配2位数或3,4,5位数的连续数字; 先返回11111, 再返回 11; 贪婪匹配
        Pattern pattern = Pattern.compile(regStr);
        Matcher matcher = pattern.matcher(content);
        while (matcher.find()) {
            System.out.println("找到: " + matcher.group(0));
        }
    }

    @Test
    public void test03() {
        String content = "a1111111aaaahello222";
        //String regStr = "1+";     // 匹配一个1或者多个1; 贪婪匹配
        //String regStr = "\\d+";   // 匹配一个或多个数字
        //String regStr = "1*";     // 匹配0个1或多个1

        String regStr = "a1?";      // 匹配a 或 a1; ?表示零个或多个
        Pattern pattern = Pattern.compile(regStr);
        Matcher matcher = pattern.matcher(content);
        while (matcher.find()) {
            System.out.println("找到: " + matcher.group(0));
        }
    }
}

贪婪、勉强、独占

在这里插入图片描述
在这里插入图片描述

// 贪婪
@Test
public void test06() {
    String content = "afooaaaaaafooa";
    /*
        分析:
           1.贪婪规则:
             先吞掉整个content进行匹配, 若匹配失败, 则吐出最后一个字符;再次匹配,重复该过程,直到匹配成功
             afooaaaaaafooa匹配失败, 然后吐出a, content=afooaaaaaafoo 匹配成功
           2.题目分析:默认是贪婪的
            .*foo这个表达式, *表示0或n个, 而.*表示1个或1+n个. 也就是可以匹配n个任意字符; 后面foo表示以foo结尾
            找到的结果为: afooaaaaaafoo
            但是观察发现content前4个字符afoo也符合.*foo表达式,为什么没有找到呢?
            因为表达式默认是贪婪的, 会将整个content吞掉进行匹配
     */
    String regStr = ".*foo";
    Pattern pattern = Pattern.compile(regStr);
    Matcher matcher = pattern.matcher(content);
    while (matcher.find()) {
        System.out.println("找到: " + matcher.group(0));
    }
    //找到 afooaaaaaafooa
}

// 勉强
@Test
public void test07() {
    String content = "afooaaaaaafooa";
    /*
        分析:
            先吞掉一个字符进行匹配, 若匹配失败, 则吞掉下一个字符再尝试匹配, 重复此过程,直到成功
            a不匹配; af不; afo不; afoo匹配成功, 此时content=aaaaaafooa 然后继续重复该流程
            a不; aa不; .....; aaaaaafoo匹配成功
     */
    String regStr = ".*?foo";
    Pattern pattern = Pattern.compile(regStr);
    Matcher matcher = pattern.matcher(content);
    while (matcher.find()) {
        System.out.println("找到: " + matcher.group(0));
    }
    //找到: afoo
    //找到: aaaaaafoo
}

// 独占
@Test
public void test08() {
    String content = "afooaaaaaafooa";
    /*
        分析:
            规则: 吞掉整个input进行唯一一次匹配(不常用)
     */
    String regStr = ".*+foo";
    Pattern pattern = Pattern.compile(regStr);
    Matcher matcher = pattern.matcher(content);
    while (matcher.find()) {
        System.out.println("找到: " + matcher.group(0));
    }
    //找不到
}

元字符 — 定位符(边界匹配符)

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

在这里插入图片描述

@Test
public void test() {
    String content = "123-abc";
    //String regStr = "[0-9]+[a-z]*"; // 匹配至少1个数字,后接任意个小写字母的字符串
    //String regStr = "^[0-9]+[a-z]*"; // 必须以至少1个数字开头,后接任意个小写字母的字符串
    //String regStr = "^[0-9]+[a-z]+$"; // 必须至少1个数字开头,后接必须至少1个小写字母结束
    String regStr = "^[0-9]+\\-[a-z]+$"; // 必须至少1个数字开头,后接必须至少1个小写字母结束
    Pattern pattern = Pattern.compile(regStr);
    Matcher matcher = pattern.matcher(content);
    while (matcher.find()) {
        System.out.println("找到: " + matcher.group(0));
    }
    找到: 123-abc
}

@Test
public void test10() {
        String content = "hello world hihello youhello uhellohello";
        // 上面三个hello后面都有空格, 所以找到3个hello,又因为最后一个hello在结束位置,所以也能匹配最后一个hello
        //String regStr = "hello\\b"; //找到: hello //表示匹配边界的hello(这里的边界是指:被匹配的字符串最后,也可以是空格的子字符串的后面)
        
        // 这里匹配机制和\\b相反,能匹配到倒数第二个hello
        String regStr = "hello\\B";  //找到:hello 找到:hello //和\\b的含义刚好相反
        Pattern pattern = Pattern.compile(regStr);
        Matcher matcher = pattern.matcher(content);
        while (matcher.find()) {
            System.out.println("找到: " + matcher.group(0));
        }
}

分组组合

捕获分组

在这里插入图片描述
体验分组

String regex1 = "dog{3}"; 
"doggg".matches(regex1); // true

// [dog]等价于[d|o|g]等价于(d|o|g)
String regex2 = "[dog]{3}"; //等价 [dog][dog][dog]; 而[dog]每次只能取其中一个字符, 所以有27种可能
"ddd".matches(regex2); // true
"ooo".matches(regex2); // true
"ggg".matches(regex2); // true
"dog".matches(regex2); // true
"gog".matches(regex2); // true
"gdo".matches(regex2); // true
// ... 共 3 * 3 * 3 = 27 种可能

// (dog)就是一个捕获组
String regex3 = "(dog){3}"; //等价(dog)(dog)(dog); 而(dog)是一个整体; 所以(dog){3}完全匹配dogdogdog
"dogdogdog".matches(regex3); // true
// 非命名分组
@Test
public void test01() {
    String content = "hanshunping s7789 nn1189han";
    //下面是非命名分组:
    //说明:
    //1.matcher.group(0)得到匹配到的字符串
    //2.matcher.group(1)得到匹配到的字符串的第一个分组内容
    //3.matcher.group(2)得到匹配到的字符串的第二个分组内容
    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("第1个分组内容= " + matcher.group(1));
        System.out.println("第2个分组内容= " + matcher.group(2));
        System.out.println("第3个分组内容= " + matcher.group(3));
    }
}

找到: 77891个分组内容= 772个分组内容= 83个分组内容= 9
找到: 11891个分组内容= 112个分组内容= 83个分组内容= 9
// 命名分组
@Test
public void test02() {
    String content = "hanshunping s7789 nn1189han";

    // 命名分组: 可以给分组取名
    String 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("第1个分组内容= " + matcher.group(1));
        System.out.println("第2个分组内容= " + matcher.group(2));
        System.out.println("第1个(命名)分组内容= " + matcher.group("g1"));
        System.out.println("第2个(命名)分组内容= " + matcher.group("g2"));
    }
}

找到: 77891个分组内容= 772个分组内容= 891(命名)分组内容= 772(命名)分组内容= 89
找到: 11891个分组内容= 112个分组内容= 891(命名)分组内容= 112(命名)分组内容= 89

非捕获分组

在这里插入图片描述

/**
 * 非捕获分组
 */
@Test
public void test03() {
    String content = "hello 程序员教育 jack 程序员老师 程序员同学";

    //String regStr = "程序员教育|程序员老师|程序员同学";
    // 上面的写法可以等价非捕获分组,不能matcher.group(1)
    // String regStr = "程序员(?:教育|老师|同学)"; // 返回: 程序员教育 程序员老师 程序员同学

    //目标: 找到程序员这个关键字,但是要求只是查找程序员教育和程序员老师 中包含有的程序员
    //String regStr = "程序员(?=教育|老师)"; // 返回: 程序员 程序员

    //目标: 找到程序员同学中的程序员, 返回它的程序员
    String regStr = "程序员(?!教育|老师)"; // 返回: 程序员

    Pattern pattern = Pattern.compile(regStr);
    Matcher matcher = pattern.matcher(content);
    while (matcher.find()) {
        System.out.println("找到: " + matcher.group(0));
    }
}

非贪婪匹配

Java中默认是贪婪匹配 (可看上面介绍的贪婪、勉强、独占)

public class Regexp07 {
    @Test
    public void test() {

        String content = "hello111111 ok";
        String regStr = "\\d+"; // + 表示1个或n个1匹配, 默认采用贪婪匹配, 匹配n个1 // 111111 无空子串
        // 如果上面不想使用贪婪匹配,可以加?
        //String regStr = "\\d*"; // 贪婪匹配; 111111 有空子串
        //String regStr = "\\d?";   // 非贪婪匹配; 1 1 1 1 1 1

        Pattern pattern = Pattern.compile(regStr);
        Matcher matcher = pattern.matcher(content);
        while (matcher.find()) {
            System.out.println("找到: " + matcher.group(0));
        }
    }
}

正则表达式练习

/**
 * Description: 正则表达式的应用实例
 *
 * @date 2022/3/27 14:23
 */
public class Regexp08Test {

    @Test
    public void test() {
        //1. 验证是否是汉字
        //String content = "张三丰";
        //String regStr = "^[\u0391-\uffe8]+$";

        //2. 邮政编码, 1-9开头的六位数
        String content = "123890";
        String regStr = "^[1-9]\\d{5}$";
        Pattern pattern = Pattern.compile(regStr);
        Matcher matcher = pattern.matcher(content);
        if (matcher.find()) {
            System.out.println("满足格式: " + matcher.group(0));
        } else {
            System.out.println("不满足格式!");
        }
    }

    @Test
    public void test02() {
        // QQ号: 要求1-9开头的(5位-10位数)
        String content = "123890";
        String regStr = "^[1-9]\\d{4,9}$";
        Pattern pattern = Pattern.compile(regStr);
        Matcher matcher = pattern.matcher(content);
        while (matcher.find()) {
            System.out.println("满足格式: " + matcher.group(0));
        }
    }

    @Test
    public void test03() {
        // 手机号;要求以 13, 14, 15, 18开头的11位数
        String content = "14839905434";
        String regStr = "^1[3|4|5|8]\\d{9}$";
        Pattern pattern = Pattern.compile(regStr);
        Matcher matcher = pattern.matcher(content);
        while (matcher.find()) {
            System.out.println("满足格式: " + matcher.group(0));
        }
    }

    @Test
    public void test04() {
        // URL验证
        String content = "https://www.bilibili.com/blackboard/activity-TVWStSTDRs.html?spm_id_from=333.851.b_62696c695f7265706f72745f6c697665.21";

        /*
           思路:
            1. 先确定url的开始部分 https:// | http://
            2. 通过([\w-]+\.)+[\w-]+ 匹配 www.bilibili.com
            3. (/[\\w-?=&/%.#]*)? 中 ?表示可能0个或1个; [\\w-?=&/%.#]* 表示: 0个或多个[\\w-?=&/%.#]; [\\w-?=&/%.#]表示 可以出现0-9a-zA-Z中的字符和-?=&/%.#
         */
        String regStr = "^((https|http)://)?([\\w-]+\\.)+[\\w-]+(/[\\w-?=&/%.#]*)?$";

        Pattern pattern = Pattern.compile(regStr);
        Matcher matcher = pattern.matcher(content);
        while (matcher.find()) {
            System.out.println("满足格式: " + matcher.group(0));
        }
    }
}

正则表达式常用类 (Pattern、Matcher)

  • Pattern类:pattern对象是一个正则表达式对象,Pattern类没有公共构造方法,创建一个Pattern对象,需要调用公共静态方法compile,它返回一个Pattern对象,该方法接受一个正则表达式作为它的第一个参数
  • Matcher类Matcher对象是对输入字符串进行解释和匹配的引擎。与Pattern类一样,Matcher也没有构造方法,你需要调用Pattern对象的matcher方法来获得一个Matcher对象。
  • PatternSyntaxException类:是一个非强制异常类,它表示一个正则表达式模式中的语法错误。

Pattern类中的matches()方法

注意: matches方法是完全匹配, 也就是说写的表达式regStr 必须整体匹配 content的内容, 否则返回false

@Test
public void test() {
    String content = "hello abc hello, 王大小";
    //String regStr = "hello"; // false
    String regStr = "hello.*"; // 表示hello后除\n可以跟任意字符
    boolean matches = Pattern.matches(regStr, content);
    System.out.println("整体匹配: matches = " + matches); // true
}

Matcher类中的常用方法
在这里插入图片描述
在这里插入图片描述

@Test
public void test01() {
    String content = "hello edu jack tom hello smith hello";
    String regStr = "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.group(0));
    }
    // 整体匹配
    System.out.println(matcher.matches()); // false
}

@Test
public void test02() {
    // 将content中的hello替换为你好
    String content = "hello edu jack tom hello smith hello";

    String regStr = "hello";
    Pattern pattern = Pattern.compile(regStr);
    Matcher matcher = pattern.matcher(content);

    String newContent = matcher.replaceAll("你好");
    System.out.println("newContent = " + newContent);
    System.out.println("content = " + content);
}

分组反向引用

在这里插入图片描述
在这里插入图片描述

/**
 * Description: 反向引用
 * @date 2022/3/28 13:17
 */
public class Regexp10 {

    @Test
    public void test() {

        String content = "hello33333 jack13317887 tom11 jack22 yyy12345 xxx";
        //String regStr = "(\\d)\\1"; // 匹配两个连续的相同数字
        //String regStr = "(\\d)\\1{4}"; // 匹配五个连续的相同数字
        String regStr = "(\\d)(\\d)\\2\\1";   // 匹配个位和千位相同, 十位和百位相同的数 5225, 1221等

        Pattern pattern = Pattern.compile(regStr);
        Matcher matcher = pattern.matcher(content);
        while (matcher.find()) {
            System.out.println("找到: " + matcher.group(0));
        }
    }

    @Test
    public void test01() {

        String content = "12345-11122233327372-999888777";

        // 满足前面是一个五位数, 然后一个-号, 后面是一个九位数,连续每三位相同
        String 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));
        }
    }

    @Test
    public void test02() {
        String content = "我...我要...学学学Java编编程!";
        // 首先替换掉.
        content = Pattern.compile("\\.").matcher(content).replaceAll("");

        // 我我要学学学Java编编程!
        // (.)表示匹配字符串中非\n的每一个字符;\\1表示每一个字符后面是否有和自己相同的字符; +表示有1个或n个和自己相同的字符
        String regStr = "(.)\\1+"; // 这个+针对于 学学学 这种情况; 如果是 要 就没有效果,因为(.)\\1都匹配不上

        // 和这个 $1 就是外部引用 满足匹配 (.)中的字符, 1为分组号
        System.out.println(Pattern.compile(regStr).matcher(content).replaceAll("$1"));
    }
}

String类中使用正则表达式

  • 替换功能
    • public String replaceAll(String regex, String replacement)
  • 判断功能
    • public boolearn matches(String regex) 底层使用Pattern和Matcher类
  • 分割功能
    • public String[] split(String regex)
 @Test
 public void test03() {
     // String 的 replaceAll方法
     String content = "JDK1.3 JDK1.4 JDK1.3 JDK1.5";
     //String newContent = content.replaceAll("JDK1[.](3|4)", "JDK");
     String newContent = content.replaceAll("JDK1.3|JDK1.4", "JDK");
     System.out.println("newContent = " + newContent);
 }

 @Test
 public void test04() {
     String content = "13539905434";
     if (content.matches("1(35|78)\\d{8}"))
         System.out.println("success");
     else
         System.out.println("falied");
 }

 @Test
 public void test05() {
     String content = "hello#abc-jack12smith~北京";
     String[] split = content.split("#|-|\\d+|~");
     for (String regStr : split) {
         System.out.println("regStr = " + regStr);
     }
 }

基础正则表达式速查表

神奇的网站 : https://www.r2coding.com/#/README

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

white camel

感谢支持~

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

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

打赏作者

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

抵扣说明:

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

余额充值