实用的正则表达式(Java)

如约而至

目录

引言: 

一.案例引入

二.正则表达式的底层实现

Pattern的compile()源码

Pattern里的match() (匹配的方法):

​编辑

fina()和gruop()

正则表达式的分组概念:

三.正则表达式的基本语法(重要)

元字符的分类

限定符:

选择匹配符 

 分组组合和反向引用符

定位符

字符匹配符 

 四.正则表达式练习


引言: 

由于正则表达式写起来给人看上去极其不友好(乱,杂),以至于很多人对这个语法是陌生的,但是,如果我们能够正确灵活的运用正则表达式,将会极大的提高我们的开发效率。

一.案例引入

我们先引入一个案例来看看。

 String context = "a 11c 8a__sda% $&*@...abcaBc";
 String regStr = "a(?i)bc"; //b不区分大小写

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

结果:

简单看一下这段代码(一些看不懂的地方不要紧,因为还没有介绍正则的内容):

  • 然后我们通过调用Pattern的compile方法返回一个比较器对象。
  • 调用比较器的方法macher进行匹配比较。
  • 通过循环找到这段字符串里满足要求的子串。


二.正则表达式的底层实现

由于作者的水平有限,这里只是简单对底层的实现进行一个介绍。

继续拿上面的案例进行介绍。

在这块,我们主要涉及到源码里的两个类: PatternMatcher

Pattern的compile()源码

 我们可以看到,确实是返回了一个new的Pattern对象。

参数介绍:

regex是我们传进来的正则表达式;

flags是一个匹配标志(源码里的介绍):

 Match flags, a bit mask that may include
         {@link #CASE_INSENSITIVE}, {@link #MULTILINE}, {@link #DOTALL},
         {@link #UNICODE_CASE}, {@link #CANON_EQ}, {@link #UNIX_LINES},
         {@link #LITERAL}, {@link #UNICODE_CHARACTER_CLASS}
         and {@link #COMMENTS}

举个例子:

CASE_INSENSITIVE : 这个标志就代表匹配时不区分大小写。

Pattern里的match() (匹配的方法):

可以看到,在这里最终调用的其实是Matcher类的Matcher()方法。这里关于源码,其实我们也没有必要完全看懂,这个东西的完全实现还是有一点复杂的。 

fina()和gruop()

  • find()方法

find()方法就是用来循环查找下一个符合正则表达式的子串的 

find()方法做的事情: 

find()方法会将成功找到的子串的始末位置记录到groups[]数组中,把开始索引的位置记录到groups[0],结束索引的位置记录到groups[1];下次执行时,直接从groups[1]的位置开始find()。 

  • group[]数组

group[]数组用来存放匹配到的字符串。但并不是将字符串存放到数组中(这明显是一个int[]),而是将匹配的子串的开始和结束索引位置存放到数组中。

 但是还是有一个关于group[]疑问:

关于这一个,我们再来写一个小例子:

匹配一个子串(前五个是字母,字母不区分大小写,后三个是数字)。

肉眼我们可以观察到:"hello123" , "lskda215" , "hello123"是符合要求的。

 public static void main(String[] args) {
        String str = "hello123abc w;orld ;a'lb'c lskda2154p;]=hello123asdx";
        
        //匹配前五个是字母,后面跟三个数字;
        String regStr = "([a-zA-Z]{5})([0-9]{3})";

        Pattern pattern = Pattern.compile(regStr);
        Matcher matcher = pattern.matcher(str);
        while (matcher.find()){
            System.out.println("匹配到的完整子串:" + matcher.group(0));
            System.out.println("匹配到的第一组子串:" + matcher.group(1));
            System.out.println("匹配到的第二组子串:" + matcher.group(2));
        }
}

 看一下运行结果:

这里我提到了分组的概念,同时我们也看到groups数组里的数字好像也跟这个分组有点关系。我们来介绍一下分组的概念


正则表达式的分组概念:

在我们的正则表达式里,一个小括号就代表一个分组(从1开始累加),0代表整个串的分组。  

这是最初的写法:

    public static void main(String[] args) {
        String str = "hello123abc w;orld ;a'lb'c lskda2154p;]=hello123asdx";
        
        //匹配前五个是字母,后面跟三个数字;
        String regStr = "[a-zA-Z]{5}[0-9]{3}";

        Pattern pattern = Pattern.compile(regStr);
        Matcher matcher = pattern.matcher(str);
        while (matcher.find()){
            System.out.println("匹配到的完整子串:" + matcher.group(0));
        }
    }

 

groups[]的简单分析: 

 由上图可知:groups数组依次存放的就是相应的索引位置。

三.正则表达式的基本语法(重要)

 说了那么多,正则表达式的语法才是实干的重要部分,如果你能够熟练运用各种元字符,底层上有一些没有搞懂又有什么关系呢?

元字符的分类

  • 限定符
  • 选择匹配符
  • 分组组合和反向引用符
  • 特殊字符
  • 字符匹配符
  • 定位符

限定符:

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

符号说明
{n,}指定至少n个匹配
{n,m}指定至少n个但不多余m个匹配
*指定字符重复0次或n次
+指定字符重复1次或n次(至少1次)
指定字符重复0次或1次
{n}只能输入n个字符

 

示例: 

[abcd]{3,}“abcd“中任意长度不小于3的字符串  : abc ,abd ,bcd ,aabbcd , dbc
[abcd]{3,5}"abcd"字母组成的任意长度不小于3,且不大于5的子串 : aaaaa, abcda , badac
(abc)*包含任意个abc(可以是0): abc , abcabc , abcabcabc....
x+(abc)*以至少一个x开头,且任意个abc结尾: x , xabc , xxabcabc , xxxxabc
x+(abc)?以至少一个x开头,且最多一个abc结尾 : xxx , xxxabc , xxxxabc
(abcd){3}由abcd中任意组成的长度为3的组合: abc ,abd ,bcd , bca , bda

  

选择匹配符 

    符号    说明     示例
      |匹配“|”之前或之后的表达式

ab|cd : ab或者cd

 分组组合和反向引用符

注:这里的"pattern"是你在实际的场景中写出的正则表达式

分组组合符:

  符号说明
(pattern)捕获pattern到默认分配的组里去,编号为零的是整个正则表达式匹配的内容
(?<name>pattern)捕获pattern到命名为"name"的组里去
(?:pattern)非捕获匹配,只匹配pattern,但不捕获该匹配的子字符串。
(?=pattern)非捕获匹配 ,只匹配pattern之前的位置
(?!pattern)非捕获匹配,只匹配非pattern之前的位置

反向 

 反向引用符号

符号 说明
\\内部反向引用使用
$外部反向引用使用

当圆括号的内容被捕获后(分组的内容),可以在这个括号后被使用,从而快速便捷的达到一些匹配的目的。

案例演示:匹配电话号码

需求:前五个是任意数字,然后- , 后面九位数,每三位都一样

    String str = "hello jerry13545-111222333 1221 tom11111 jerry";


        String regStr = "\\d{5}-(\\d)\\1{2}(\\d)\\2{2}(\\d)\\3{2}";

        Pattern pattern = Pattern.compile(regStr);
        Matcher matcher = pattern.matcher(str);

        while (matcher.find()){
            System.out.println("匹配到:" + matcher.group(0));
        }
    }

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

"(\\d)"被捕获后,由于这是我们的第一个分组,“\\1”就是引用第一组的内容;

后面的{2},代表除了已经出现的一次,再出现两次,一共三次。

关于外部反向引用的$,我们在后面的一个例子中举例说明。

定位符

符号说明
^指定起始字符
$指定结束字符
\\b匹配目标字符串的边界(一般是挨着空格)
\\B匹配目标字符串的非边界(和\\b相反)

字符匹配符 

符号说明
.匹配除\n以外的任何字符
\\d匹配单个数字字符
\\D匹配单个非数字字符
\\w匹配任意英文字母,数字还有下划线
\\W匹配特殊字符,与\\w相反
[]可接受的字符列表
[^]不接受的字符列表
-连字符

 四.正则表达式练习

外部反向引用的实例。

题目:将 “我我我...要...学学学学学..编...程程.程....Ja..v..a” 恢复正常 “我要学编程Java!”

  public static void main(String[] args) {
        String str = "我我...要要学学学学....编..程Java!";

        //第一步,先去除"."
        Pattern pattern = Pattern.compile("\\.");
        Matcher matcher = pattern.matcher(str);
        while (matcher.find()){
            str = matcher.replaceAll("");
        }
        System.out.println("去除.后:  " + str);

        //第二步,去除重复的字
        pattern = pattern.compile("(.)\\1+");
        matcher = pattern.matcher(str);
        while (matcher.find()){
            str = matcher.replaceAll("$1");
        }
        System.out.println("净化完成后的:  "+ str);
    }

关于正则表达式,需要大家多多熟悉,多多练习,本文也只是为大家介绍了一部分。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值