强大的正则表达式学习总结

/ 默认分类 / 0 条评论 / 852浏览

之前格式化一些数据的时候经常会使用到一些简单的正则表达式来匹配,然后加上idea的替换,可以很方便的得到想要的数据格式,但是一直没有完整的总结过正则表达式的语法,正则表达式是我个人觉得特别牛逼的一个语法功能,当然想用的好,熟练,复杂太太太难了,不过我只会一些简单的,这已经够我在工作和学习中使用了.

下面是总结的正则表达式的基本语法,以及java语言中的使用

?	    零次或一次匹配前面的字符或子表达式,相当于{0,1}
*	    零次或多次匹配前面字符或子表达式,相当于{0,}
+	    一次或多次匹配前面字符或子表达式,相当于{1,}
{n}	    匹配n次
{n,}	匹配至少n次
{n,m}	匹配n到m次
.	除换行符外的所有字符
\w	匹配所有字母数字,等同于[a-zA-Z0-9_]
\W	匹配所有非字母非数字,即符号,等同于[^\w]
\b	匹配一个单词边界(boundary)
\B	匹配一个非单词边界
\d	匹配数字,等同于[0-9]
\D	匹配非数字,等同于[^\d]
\s	匹配所有空格字符,等同于[\t\n\f\r\p{Z}]
\S	匹配所有非空格字符,等同于[^\s]
\f	匹配一个换行符
\n	匹配一个换行符
\r	匹配一个回车符
\t	匹配一个制表符
\v	匹配一个垂直制表符
\p	匹配 CR/LF (等同于 \r\n),用来匹配 DOS 行终止符
.	句号匹配任意单字符,换行符除外
[]	字符种类, 匹配方括号内的任意字符
[^]	否定的字符种类. 匹配除了方括号里的任意字符
*	匹配>=0个重复的在*号之前的字符
+	匹配>=1个重复的+号前的字符
?	标记?之前的字符为可选
{n,m}	匹配num个大括号之前的字符 (n <= num <= m)
(xyz)	字符集, 匹配与 xyz 完全相等的字符串,匹配小括号内的字符串,可以是一个,也可以是多个,常跟“|”(或)符号搭配使用,是多选结构
|	或运算符, 匹配符号前或后的字符
\	转义字符, 用于匹配一些保留的字符 [ ] ( ) { } . * + ? ^ $ \ |
^	从开始行开始匹配
$	从末端开始匹配

方括号在正则中表示一个区间,我们称它为字符集。

字符组中的字符集合只是所有的可选项,最终它只能匹配一个字符

字符组是一个独立的世界,元字符不需要转义。但有两个字符在字符组中有特殊含义。

^在字符组中表示取反,不再是文本开始的位置了。

-本来是一个普通字符,在字符组中摇身一变成为连字符(只有两种字符是可以用连字符的:英文字母和数字。)

  1. 匹配字符串中是否存在指定串

这里我用的是find,不是matches,find是指在这个串中查找看是否又能匹配上的部分,但是matches是针对整个串,只有整个串都匹配上了,才会返回true

        String testStr = "hahah12zuoxixi";
        String regex = ".zuo.";
        Pattern pattern = Pattern.compile(regex);
        Matcher matcher = pattern.matcher(testStr);
        boolean result = matcher.find();
        System.out.println("=============");
        System.out.println("testStr:"+testStr);
        System.out.println("regex:"+regex);
        System.out.println("result:"+result);
=============
testStr:hahah12zuoxixi
regex:.zuo.
result:true

. (英文句号或叫点)表示匹配任意单字符,换行符除外,因为zuo的前后都有非换行符的单字符存在,所以匹配到了

testStr:hahah12
zuoxixi
regex:.zuo.
result:false

上面这样的,也就是String testStr = "hahah12\nzuoxixi" 因为左边第一个就是换行符,点又是匹配单字符,所以匹配不上

如果是这样的串: String testStr = "hahah1\n2zuoxixi" 那么result就是true,这种就会匹配上,因为左边第一个单字符不是换行符了

如果想指定单个字符出现的次数,可以使用匹配次数的正则,下面的就是标识除换行符外的单个字符出现的次数大于等于2次

String testStr = "hahah\n12zuoxixi";
=============
testStr:hahah
12zuoxixi
regex:.{2}zuo.
result:true

但是如果这样就不行了,因为只出现了1次,如下:

String testStr = "hahah1\n2zuoxixi";
=============
testStr:hahah1
2zuoxixi
regex:.{2}zuo.
result:false

下面这样,是从整个串中直接查找,不考虑必须是以这个数据作为开头或者以这个数据作为结尾

=============
testStr:hahah12zuoxixi
regex:.{2}zuo.
result:true

但是如果是下面这样,因为不是以匹配的数据作为开头,所以不行。

=============
testStr:hahah12zuoxixi
regex:^.{2}zuo.
result:false

如果修改为这样,那么就没有问题了:

=============
testStr:12zuohahah12zuoxixi
regex:^.{2}zuo.
result:true

拓展:{n,}表示>=n次出现

=============
testStr:111112zuohahah12zuoxixi
regex:^.{2}zuo.
result:false
=============
testStr:111112zuohahah12zuoxixi
regex:^.{2,}zuo.
result:true
=============
testStr:111112zuohahah12zuoxixi
regex:^.{2,}zuo.{2}$
result:false
=============
testStr:111112zuohahah12zuoxixi
regex:^.{2,}zuo.{2,}$
result:true
  1. 查找提取匹配到的数据

可以将find放在循环中,并是呀group可以返回匹配到的部分数据,如下:

String testStr = "12zuohahah12zuoxixi";
        String regex = ".{2}zuo.";
        Pattern pattern = Pattern.compile(regex);
        Matcher matcher = pattern.matcher(testStr);
        System.out.println("=============");
        System.out.println("testStr:"+testStr);
        System.out.println("regex:"+regex);
        boolean matches = false;
        while (matcher.find()){
            String group = matcher.group();
            System.out.println("找到:"+group+",start:"+matcher.start()+",end:"+matcher.end());
            matches = true;
        }
        if(!matches){
            System.out.println("没有找到");
        }
=============
testStr:12zuohahah12zuoxixi
regex:.{2}zuo.
找到:12zuoh,start:0,end:6
找到:12zuox,start:10,end:16

使用过程中的问题记录:

问题1:匹配提取括号内的数据

=============
testStr:Dear user, {billername} top 
regex:.*\{.*}.*
找到:Dear user, {billername} top ,start:0,end:28

可以看到,这里很明显,不是我们想要的因为,我只想得到大括号内的billerName,这里容易想到,直接将前后的.*去掉

=============
testStr:Dear user, {billername} top 
regex:\{.*}
找到:{billername},start:11,end:23

但是这里还是包括了括号,这不是我们想要的

并且,如果我们要匹配多个,还用上面的正则的话,就会出现头尾的括号匹配生效的情况,如下:

=============
testStr:Dear user, {billername} top up is 54zh{currently} unavailable 
regex:\{.*}
找到:{billername} top up is {currently},start:11,end:45

正确的做法应该是使用零宽度断言(前后预查),关于这个语法点可以参考下正则表达式github开源教程,这里我直接贴出来正确的匹配正则:

=============
testStr:Dear user, {billername} top up is 54zh{currently} unavailable 
regex:(?<=\{)[^}]+
找到:billername,start:12,end:22
找到:currently,start:35,end:44

解释: 前面的小括号内的就是正后发断言,?<=\{表示以{开头的(需要转义,写在java代码中应该是两个反斜杠,如果你不知道转义符怎么写,idea的智能提示可以帮助你,或者在idea中直接将正则复制到字符串中,这样idea会自动补全转义),之后一直往后匹配,只要不是},也就是说匹配结束标志就是},之后再以相同的规则继续.所以这里就提取出了billername和currently ps:最后的+号表示满足条件>=1次,上面的基础语法中有介绍 如果只要替换出54zh开头的后面的{}中的,也就是currently,那么也很简单,直接变更下开头就好了,如下:

=============
testStr:Dear user, {billername} top up is 54zh{currently} unavailable 
regex:(?<=54zh\{)[^}]+
找到:currently,start:39,end:48

正则表达式github开源教程

正则在线练习

参考文章