• 正则表达式(Java)


    正则表达式

    - regular expression => RegExp

    1、底层实现 :

    1.1 什么是分组:(例如:2023)

    1.不分组:\\d\\d\\d\\d

    2.分组:(\\d\\d)(\\d\\d),正则表达式中有 () 表示分组,一个括号为一组

    1.2 matcher.find():完成的任务

    1.根据指定的规则,定位满足规则的子字符串

    2.找到后,将子字符串的开始的索引记录到 matcher 对象的属性 int groups;

    a.不分组:(例如:2023)

    i.把该子字符串开始位置的索引记录到 groups[0] = 0 中 ,结束的索引+1的值记录到 groups[1] = 4中;

    b.分组:(例如:(20)(23))

    i.把该子字符串开始位置的索引记录到 groups[0] = 0 中 ,结束的索引+1的值记录到 groups[1] = 4中;

    ii.记录 1 组 () 匹配的字符串 groups[2] = 0,groups[3] = 2

    iii.记录 2 组 () 匹配的字符串 groups[4] = 2,groups[5] = 4

    iv.如果有更多的分组以此类推

    3.同时记录 oldLast 的值为子字符串结束位置的索引+1,即下次执行 find 方法时,就从 lodLast 开始

    a.例如 groups[0] = 0 和 groups[1] = 4 的记录的位置,oldLast = 4,从 content 开始截取子字符串返回,就是 [0, 4) 左闭右开

    b.如果再次执行 find 方法,仍然按上面的执行

    1.3 小结:

    1.如果正则表达式有 () 即分组

    2.取出匹配的字符串规则如下:

    a.group(0) 表示匹配到的子字符串

    b.group(1) 表示匹配到的子字符串的第一组子串

    c.group(2) 表示匹配到的子字符串的第二组子串

    d.以此类推,但是不能越界

    1.4 例子:

    String content = "……";

    // 1、\\d 表示一个任意的数字

    //String regStr = "\\d\\d\\d\\d";

    String regStr = "(\\d\\d)(\\d\\d)";

    // 2、创建模式对象[即正则表达式对象]

    Pattren pattern = Pattern.compile(regStr);

    // 3、创建匹配器

    // 说明:创建匹配器 matcher,按照正则表达式的规则去匹配 content 字符串

    Matcher matcher = Pattern.matcher(content);

    // 4、开始匹配

    while(matcher.find()){

        System.out.println("找到:" + matcher.group(0));

        System.out.println("第一组为:" + matcher.group(1));

        System.out.println("第二组为:" + matcher.group(2));

    }

    2、语法:

    - 元字符(Metacharacter)- 转义字符 \\

    - \\ 符号 说明:

    - 在使用正则表达式去检索某些特殊字符时,需要用到转义字符,否则检索不到结果,甚至报错

    - 在 Java 中的正则表达式这个,两个 \\ 代表其他语言中的一个 \

    - 需要用到转义符号的字符有:. * + ( ) $ / \ ? [ ] ^ { }

    例如:

    匹配(:\\(

    匹配.:\\.

    2.1 限定符

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

    符号含义示例说明匹配输入*指定字符重复 0 次或 n 次(无要求) 0 到多(abc)*仅包含任意个 abc 的字符串,等效于 \w*abc、abcabcabc+指定字符重复 1 次或 n 次(至少一次) 1 到多m+(abc)*以至少 1 个 m 开头,后接任意个 abc 的字符串m、mabc、mabcabc?指定字符重复 0 次或 1 次(最多一次) 0 到 1m+abc?以至少 1 个 m 开头,后接 ab 或 abc 的字符串mab、mabc、mmmab、mmabc{n}只能输入 n 个字符[abcd]{3}由 abcd 中字母组成的任意长度为 3 的字符串abc、 dbc、adc{n,}指定至少 n 个匹配[abcd]{3,}由 abcd 中字母组成的任意长度不小于 3 的字符串aab、dbc、aaabdc{n,m}指定至少 n 个但不多于 m 个匹配[abcd]{3,5}由 abcd 中字母组成的任意长度不小于 3,不大于 5 的字符串abc、abcd、aaaaa、bcdab

    - 细节:

    - 非贪婪匹配和贪婪匹配:

    ?当此字符紧随任何其他限定符 (*、+、?、{n}、{n,m}) 之后时,匹配模式是 "非贪心的"。"非贪心的" 模式匹配搜索到的、尽可能短的字符串,而默认的 "贪心的" 模式匹配搜索到的、尽可能长的字符串。例如,在字符串 "oooo" 中,"o+?" 只匹配单个 "o",而 "o+" 匹配所有 "o"。

    String content = "hello111111 ok";

    //String regStr = "\\d+"; //默认是贪婪匹配

    String regStr = "\\d+?"; 1/非贪婪匹配

    Pattern pattern = Pattern.compile(regStr);

    Matcher matcher = pattern.matcher(content);

    while (matcher.find()) {

        System.out.println("找到: " + matcher.group(0));

    }

    2.2 选择匹配符

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

    符号含义示例解释|匹配 “|” 之前或之后的表达式ab|cdab 或者 cd

    2.3 分组组合和反向引用符:

    2.3.1 分组、捕获和反向引用的概念:

    1.分组:

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

    2. 捕获:

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

    3.反向引用:

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

    2.3.2 捕获分组:

    常用分组构造形式说明(pattern)非命名捕获。捕获匹配的子字符串。编号为零的第一个捕获是由整个正则表达式模式匹配的文本,其它捕获结果则根据左括号的顺序从 1 开始自动编号。(?pattern)命名捕获。将匹配的子字符串捕获到一个组名称或编号名称中。用于 name 的字符串不能包含任何标点符号,并且不能以数字开头。可以使用单引号替代尖括号,例如(?'name')

    // 下面就是非命名分组

    // 说明

    // 1. matcher.group(0) 得到匹配到的字符串

    // 2. matcher.group(1) 得到匹配到的字符串的第 1 个分组内容

    // 3. matcher.group(2) 得到匹配到的字符串的第 2 个分组内容

    // String regStr = "(\\d\\d)(\\d\\d)";//匹配 4 个数字的字符串

    //命名分组: 即可以给分组取名

    String regStr = "(?\\d\\d)(?\\d\\d)"; // 匹配 4 个数字的字符串

    I

    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"));

    }

    2.3.3 非捕获分组:

    常用分组构造形式说明(?:pattern)匹配 pattern 但不捕获该匹配的子表达式,即它是一个非捕获匹配,不存储供以后使用的匹配。这对于用 "or" 字符(|) 组合模式部件的情况很有用。例如,'industr(?:ylies) 是比 'industry | industries' 更经济的表达式。(?=pattern)它是一个非捕获匹配。例如,'Windows (?=95|98|NT|2000)’ 匹配 "Windows 2000" 中的 "Windows",但不匹配 "Windows 3.1" 中的 "Windows"。(?!pattern)该表达式匹配不处于匹配 pattern 的字符串的起始点的搜索字符串。它是一个非捕获匹配。例如,'Windows (?!9598|NT|2000)' 匹配 "Windows 3.1" 中的 "Windows",但不匹配"Windows 2000"中的 "Windows"。

    2.3.4 反向引用:

    举例:

    1、要匹配两个连续的相同数字:

    1.(\\d)\\1

    2、要匹配五个连续的相同数字:

    1.(\\d)\\1{4}

    3、要匹配个位与千位相同,十位与百位相同的数:

    1.如 1221、2332:

    2.(\\d)(\\d)\\2\\1

    4、在字符串中检索商品编号:

    1.形式如:12321-333999111,这样的号码:

    2.要求满足前面是一个五位数,然后一个 - 号,然后是一个九位数,连续的每三位都要相同

    3.\\d{5}-(\\d)\\1{2}(\\d)\\2{2}(\\d)\\3{2}

    5、结巴去重案例:

    1.去掉 . 和重复的字

    String content = "我....我要....学学学学学....编程Java";

    System.out.println("content=" + content);

    // 1、去掉所有的.

    Pattern pattern = Pattern.compile("\\.");

    Matcher matcher = Pattern.matcher(content);

    // 2、去掉重复的字

    // 思路:

    //(1)使用 (.)\\1+

    //(2)使用反向引用 $1 来替换匹配到的内容

    // 注意:因为正则表达式变化,所以需要重置 matcher

    // pattern = Pattern.compile("(.)\\1+"); // 分组的捕获内容记录到 $1

    // matcher = Pattern.matcher(content);

    // while(matcher.find()){

    // System.out.println("找到=" + matcher.group(0));

        

    // }

    // 使用 反向引用 $1 来替换匹配到的内容

    // content = matcher.replaceAll("$1");

    // System.o

  • 相关阅读:
    2024年华为OD机试真题- 手机App防沉迷系统-(C++)-OD统一考试(C卷D卷)
    比Selenium更优秀的playwright介绍与未来展望
    华为的仓颉和ArkTS这两门语言有什么区别
    【电路笔记】-脉冲宽度调制(PWM)与电机转速控制
    windows下通过远程桌面访问linux图形界面
    Linux常用命令
    nacos网关
    Windows保护模式(八)TLB&控制寄存器
    【OpenGL】七、混合
    Docker(1)
  • 原文地址:https://blog.csdn.net/weixin_46682106/article/details/133994569