当前位置: 首页 > article >正文

Java 正则基础

本文里简单的记录了一下Java正则的内容,因为之前学了python版的,所以零基础的可以去看一下视频:正则专题。而且没有列出正则里的其他方法,需要的可以百度一下

快速入门

class RegExp {
    public static void main(String[] args) {
        String content = "sdfja东方红2001都是你的sdjs1998dfSDK200djfsd2024";
        // 目标:匹配content里所有的四位连续数字
        // 1。\\d表示一个任意的数字
        String regStr = "\\d\\d\\d\\d";
        // 2.创建模式对象【即正则表达式对象】
        Pattern pattern = Pattern.compile(regStr);
        // 3.创建匹配器
        // 创建匹配器matcher,按照正则表达式的规则 去匹配 content字符串
        Matcher matcher = pattern.matcher(content);
        // 4.开始匹配
        while (matcher.find()) {
            System.out.println("找到:" + matcher.group(0));
        }
    }
}

正则底层实现

代码中的解析部分可以自己debug进行验证

class RegExp {
    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平台的企业版),应" +
                "用3443于基于Java的应用服务器。Java2平台的发布,是Java发展过程中最重要的一个" +
                "里程碑,标志着Java的应用开始普及9889";
        // 目标:匹配content里所有的四位连续数字
        // 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() 完成的任务:
            * 1.根据指定的规则pattern(这里是要求四位连续数字\\d\\d\\d\\d),
            * 定位满足规则的子字符串(比如1998)
            * 2.找到后,将子字符串的起始索引记录到matcher对象的属性 int[] groups; 中,即groups[0]=0,
            * 把该子字符串的结束索引+1的值也记录到groups属性中,即 groups[1] = 4
            * 3.同时记录oldLast 属性的值为 子字符串的结束索引+1的值,即oldLast=4,
            * 下次执行find()方法时,就从oldLast开始匹配
         * 
         * matcher.group()方法
         * 通过查看matcher.group()方法的源码可以知道
         * matcher.group(0)就是根据 groups[o]=0 和 groups[1]=4 的记录的位置,
         * 从content开始截取子字符串返回,就是[0, 4)包含0但是不包含索引为4的位置
         */
        /**
         * matcher.group()方法既然能传入一个0,也可以传入其他数字,但是传入其他数字是有前提的
         * 那就是只有匹配规则中涉及到分组时才可以传入0以外的数字,表示的是第几个分组
         * 
         * 假如把regStr改为 "(\\d\\d)(\\d\\d)", 即String regStr = "(\\d\\d)(\\d\\d)";
         * 就表示有两个分组,一对小括号就代表一个分组
         * 
         * 那么此时第一次进入while循环
         * find()方法
         * 1.根据指定的规则pattern((\\d\\d)(\\d\\d)),定位到第一个满足规则的子字符串1998
         * 2.找到后,把1998这个子字符串的起始索引和结束索引+1记录到groups数组中,
            * 即groups[0]=0, groups[1] = 3 + 1 = 4
            * 把第一个分组19的起始索引(0)和结束索引加1(1+1=2)也记录到groups数组中
            * 即groups[2]=0, groups[3] = 2
            * 把第二个分组98的起始索引(2)和结束索引加1(3+1=4)也记录到groups数组中
            * 即groups[4]=2, groups[5] = 4
            * 如果还有第三个、第四个...分组,依次记录到groups的6,7,8,9...的位置
         * 3.同时记录oldLast 属性的值为 子字符串的结束索引+1的值,即oldLast=4,
         * 下次执行find()方法时,就从oldLast开始匹配
         * 
         * matcher.group()方法
         * 运行matcher.group(0)返回的就是1998,
         * 运行matcher.group(1)返回的就是第一个分组19
         * 运行matcher.group(2)返回的就是第二个分组98,如果还有更多分组,可以传入3,4,5...等
         
         * 第二次进入while循环
         * find()方法
         * 1.根据指定的规则定位到第二个满足规则的子字符串1999
         * 2.找到后,把1999这个子字符串的起始索引和结束索引+1记录到groups数组中,
            * 即groups[0]=31, groups[1] = 34 + 1 = 35
            * 把第一个分组19的起始索引(31)和结束索引加1(32+1=33)也记录到groups数组中
            * 即groups[2]=31, groups[3] = 33
            * 把第二个分组99的起始索引(33)和结束索引加1(34+1=35)也记录到groups数组中
            * 即groups[4]=33, groups[5] = 35
            * 如果还有第三个、第四个...分组,依次记录到groups的6,7,8,9...的位置
         * 3.同时记录oldLast 属性的值为 子字符串的结束索引+1的值,即oldLast=35,
         * 下次执行find()方法时,就从oldLast开始匹配
         * 
         * matcher.group()方法,看源码可知
         * 运行matcher.group(0)返回的就是1999,
         * 运行matcher.group(1)返回的就是第一个分组19
         * 运行matcher.group(2)返回的就是第二个分组99,如果还有更多分组,可以传入3,4,5...等
         * 
         * 再循环重复上述过程
         */
        while (matcher.find()) {
            System.out.println("找到:" + matcher.group(0));
        }
    }
}

Java正则不区分大小写

java正则表达式默认是区分字母大小写的,以下写法可以实现不区分大小写:

  • (?i)abc表示abc都不区分大小写
  • a(?i)bc表示bc不区分大小写
  • a((?i)b)c表示只有b不区分大小写
  • Pattern pat = Pattern.compile(regEx, Pattern.CASE_INSENSITIVE);

元字符

  • 转义符\\:当需要使用正则去匹配某些特殊字符时,需要对这些特殊字符进行转义,否则编译报错.。比如需要匹配小括号(,就需要写成\\(

常见的特殊字符有:.*()+/\$?[]^{}

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

  • 选择匹配符|:在匹配某个字符串的时候是选择性的,即既可以匹配这个,又可以匹配那个,这时需要用到选择匹配符号|

  • 字符匹配符

在中括号[]里的字符表示的都是字符本身的含义,不再是限定符,比如[?.]中的问号和点,都表示是个问号和点本身,即在中括号里不需要转义符进行转义。

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

分组、捕获、反向引用

分组和捕获

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

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

  • 捕获分组

  • 非捕获分组:见视频非捕获分组

反向引用

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

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

public class Test {
    public static void main(String[] args) throws InterruptedException {
        String content = "11 21 33333 1991 2000";
        // 1.要匹配两个连续的相同数字 =>(\\d)\\1
        String regStr1 = "(\\d)\\1";
        Pattern compile1 = Pattern.compile(regStr1);
        Matcher matcher1 = compile1.matcher(content);
        if (matcher1.find()) {
            System.out.println("问题1:" + matcher1.group(0));
        }
        // 2.要匹配五个连续的相同数字 => (\\d)\\1{4}等价于 (\\d)\\1\\1\\1\\1
        String regStr2 = "(\\d)\\1{4}";
        Pattern compile2 = Pattern.compile(regStr2);
        Matcher matcher2 = compile2.matcher(content);
        if (matcher2.find()) {
            System.out.println("问题2:" + matcher2.group(0));
        }
        // 3.要匹配个位与千位相同,十位与百位相同的数5225,1551 => (\\d)(\\d)\\2\\1
        String regStr3 = "(\\d)(\\d)\\2\\1";
        Pattern compile3 = Pattern.compile(regStr3);
        Matcher matcher3 = compile3.matcher(content);
        while (matcher3.find()) {
            System.out.println("问题2:" + matcher3.group(0));
        }

        // 把类似“我...我要...学学学学...Java编程” 通过正则表达式修改成“我要学Java编程"
        content = "我...我要...学学学学...Java编程";
        // 1.先去掉所有的...
        content = Pattern.compile("\\.*").matcher(content).replaceAll("");
        System.out.println("去掉...之后:" + content);
        // 2.匹配出所有重复的文字
        // (.)\\1+ 中,.表示匹配任意一个字符,然后用反向引用查看.匹配的字符是否重复
        Matcher matcher = Pattern.compile("(.)\\1+").matcher(content);
        while (matcher.find()) {
            // 分别打印出我我和学学学学
            System.out.println("重复的文字有:" + matcher.group(0));
        }
        // 3.去重
        content = matcher.replaceAll("$1");
        System.out.println(content);
    }
}

非贪婪匹配

Java正则默认是贪婪匹配的,如果想要实现非贪婪匹配,只需要在其他限定符后加英文问号


http://www.kler.cn/a/378873.html

相关文章:

  • 读数据工程之道:设计和构建健壮的数据系统28数据服务常见关注点
  • 【网络安全】开发中存在的重定向与Referer问题
  • gradlew.cmd的使用
  • P11233 [CSP-S 2024] 染色 题解
  • 哔哩哔哩车机版2.7.0|专为司机打造的车机版B站,内容丰富,功能齐全
  • 计算机毕业设计——ssm基于JAVA的求职招聘网站的设计与实现演示录像 2021
  • 生成对抗网络(GAN)如何推动AIGC的发展
  • MacOS如何读取磁盘原始的扇区内容,恢复误删除的数据
  • 【IC每日一题--单bitCDC跨时钟和同步FIFO】
  • [ 应急响应靶场实战 ] VMware 搭建win server 2012应急响应靶机 攻击者获取服务器权限上传恶意病毒 防守方人员应急响应并溯源
  • ssm基于vue搭建的新闻网站+vue
  • Python+Selenium+Pytest+POM自动化测试框架封装
  • 机器学习的模型评估与选择
  • Msys mingw32编译报错 CMake Error: Could not create named generator MSYS Makefiles
  • DIP(Deep Image Prior,深度图像先验)和DMs(Diffusion Models,扩散模型)
  • CytoSPACE·单细胞与空间转录组的高精度对齐
  • API网关 - JWT认证 ; 原理概述与具体实践样例
  • 【06】A-Maven项目SVN设置忽略文件
  • 编写高性能爬虫抓取股票行情数据
  • Vue学习之路15----Props
  • 华为鸿蒙应用开发
  • 百度如何打造AI原生研发新范式?
  • 双向链表及如何使用GLib的GList实现双向链表
  • b站小土堆PyTorch视频学习笔记(CIFAR10数据集分类实例)
  • javascript 字符串转json格式数组
  • nginx系列--(三)--http