正则简介
几乎每一种编程语言都支持正则表达式( Regular Expression ),它定义一些强大的规则,可以用于字符串的匹配、查找、替换。除了一些细节之外, 每门编程语言的正则基本一致 。在这些编程语言之外,idea也支持正则表达式来操作字符串。本文所有正则示例都是在idea下运行的。
匹配单个字符
普通字符
输入什么字符就匹配什么字符,输入字母a就匹配字母a,输入字母b就匹配字母b,输入_就匹配_。
按快捷键ctrl + r,点击.*,就能在idea中使用正则的语法来操作字符串了,匹配到的结果背景会变成黄色,.*右侧提示一共产生了多少个匹配结果以及光标现在位于第几个匹配结果,点击↑ ↓可以在匹配结果之间进行跳转。
字符类元字符
\w匹配大小写字母、数字和下划线,w是word的缩写,单词的意思。编程的变量命名不仅有字母,还有数字和下划线,所以数字和下划线也归到了word这一部分。
\W是对\w的取反,匹配\w外的任意字符。
\d匹配任一数字(0到9),d是digit的缩写,数字的意思。
和\W类似,\D是对\d的取反,匹配\d外的任意字符。
\s匹配空白字符,包括空格,制表符,换行符等,s是单词space的缩写,空白字符的意思。如下图中,\s匹配到了_后面的换行符,也匹配9和5之前的空格。
\S是对\s的取反,匹配\s外的任意字符。
.匹配除换行符外的任一字符,匹配结果如下图,除了_后面的换行符没有匹配到,其他字符都被匹配到了。
如果上面的这些特殊的字符集合都无法满足需求,还可以使用[]自定义字符集合,比如我现在只想匹配大写字母和小写字母,那么自定义的集合就是[A-Za-z],A-Z表示的是一个区间,指所有的大写字母,a-z指所有的小写字母,类似的表达还有0-9等。
特殊字符需要转义
前面的很多元字符都是以\开头的,那么如果我想匹配\字符应该怎么做呢?可以直接输入\字符看一下结果。
idea直接提示了这个一个错误的模式,如果想要匹配\字符,需要用\转义一下,如下图:
匹配多个字符
匹配多个字符就是前面的单个字符加上量词元字符,普通的量词元字符需要使用{}。
匹配n次,直接在{}里面写要匹配的次数即可,比如下面匹配3个a。
至少匹配n次,在{}里面写{n,},比如下面至少匹配两个a。
匹配n到m次,在{}里面填写{n,m},比如下面匹配a两到三次。
除了自己手动指定次数外,一些常用的次数可以用特殊的量词元字符表示。
*表示匹配零次或多次,相当于{0,},匹配结果如下,可以看到两者的匹配结果是一样的。
+表示匹配至少一次,相当于{1,},匹配结果如下:
?表示匹配0次或1次,下图的匹配模式是ab?,b可以被匹配1次,也可以不被匹配,所以能够被匹配的字符是a,或者ab。
匹配位置
正则中除了匹配字符之外,还可以匹配位置,比如一行的行首、一行的行尾、单词边界等,下面逐个介绍
^匹配每一行行首,可以看到这两行的行首有一条淡黄色的细线,第一行因为被光标覆盖,所以显示是黑色细线。
$匹配每一行的行尾。
\b匹配单词边界,所谓单词边界就是\w字符与\W字符交界的位置。
其他元字符
^在匹配位置时表示匹配行首,但是如果放到[]中,就是表示对[]里面的字符集合取反,比如对之前的字母取反。
使用?取消贪婪匹配。假如有下面需求,想从<h1> hello world </h1>中匹配html标签<h1>或者</h1>,首先写<.*>匹配标签,但是出现了意想不到效果,<h1> hello world </h1>整个文本都匹配到了,这是因为正则的匹配是尽可能多的匹配,所以在匹配到了<h1>时并不会停下来,而是继续看看还有没有符合规则的,然后发现<h1> hello world </h1>这个整体也是符合<.*>这个规则的,所以又进行了匹配,这种默认的匹配模式就是贪婪模式,如果想要取消这种贪婪模式,可以在模式后添加一个?号,这样当出现第一个匹配的结果时,就会停下来。
分组捕获
在正则中表达中,分组允许我们将多个字符当作一个整体单元。分组有两个作用,一是和量词元字符结合使用,二是替换字符串时,反向引用这个匹配的整体。
和量词元字符结合
和量词元字符结合使用,如下图,匹配重复abc的部分。
反向引用
假如有下面的需求,我想将下面两种语言的hello world都替换成你好世界,它们的h1h2标签保持不变,这就需要用到反向引用了。
这里用<.*?>匹配了<h1>和<h2>,用<.*>匹配了</h1>和</h2>,用.*匹配标签里面的内容,用()把匹配结果当做一个整体。使用$加数字来引用前面的匹配结果,$1指的就是<h1>或<h2>,$2指的就是</h1>或</h2>
点击Repalce All替换后的结果如下:
断言
正则中还有一些类似逻辑判断的操作,它们是零宽正向先行断言,零宽负向先行断言,零宽正向后行断言, 零宽负向后行断言。
下面是菜鸟教程对正则先行断言的解释,因为我始终无法理解先行断言为啥是在匹配位置之后,所以接下来的先行断言和后行断言我会按照自己的理解来解释,它可能是错误,提前在这里声明一下
先行断言(Lookahead Assertions)
检查当前位置之后是否匹配特定模式
所谓零宽指操作发生的是位置,而不是字符,位置类似于前面提到的位置字符^、$、\b。
所谓正向就是和模式匹配成功才算结果成功,正向相关的字符是=,负向就是和模式匹配不成功才算结果成功,类似于取反操作,负向相关的字符是!
先行就是说匹配发生前面,前面是什么意思呢?因为文字书写的方向是从左到右,所以右边就是文字方向的前面,左侧就是文字方向的后面。
零宽正向先行断言
零宽正向先行断言指的就是字符右侧匹配成功算结果成功。
下面有一行文本a regular expression,我想匹配右侧是gular的re字符,忽略掉右侧是ssion的re字符,正则的写法是re(?=gular),这里我会把?理解成断言相关的字符,?表示问个对错。
零宽负向先行断言
零宽负向先行断言指的就是字符右侧匹配不成功算结果成功。
还是之前的文本,我想匹配右侧是不是gular的re字符,正则的写法是re(?!gular)
零宽正向后行断言
零宽正向后行断言指的就是字符左侧匹配成功算结果成功。
下面有一行文本regex represents regular expression,我想匹配单词中间的re,那么re就不能是单词的开头,左侧一定是有字母的,所以正则的写法是(?<=\w)re。这里<我会把它理解成左侧的意思,单纯看它箭头的方向也是指左。
零宽负向后行断言
零宽负向先行断言指的是字符左侧匹配不成功算结果成功。
还是之前的文本,我想匹配作为单词开头的re,那么re前面就不能有任何字母,所以正则的写法是(?<!\w)r。