今天看了一下正则表达式,早就看这东西不顺眼了,以前也是看过一些基础语法,可惜时间长了,也没有动手写过,都给忘了,好记性不如烂笔头,还是要做好笔记啊!
啥是正则表达式?
- 正则表达式(Regular Expression)是一种文本模式,包括普通字符(例如,a 到 z 之间的字母)和特殊字符(称为"元字符")。
- 正则表达式使用单个字符串来描述、匹配一系列匹配某个句法规则的字符串。
说白了,这玩意就是一个用来匹配字符串的工具,它十分灵活,用好了轻则提升工作效率,重则轻轻松松上王者。
从示例开始
最简单的正则表达式就是要匹配的字符本身,比如我要匹配man这个单词,就可以用man这个正则表达式。但是这样没啥意思,索然无味。下面看一个简单的正则表达式
hello\s\d+
它可以匹配hello 123,也可以匹配hello 153212,怎么样,是不是真香!
元字符
这个元字符不必纠结是啥,他就跟Java的关键字类似,规定的特殊意义的字符。元字符按照我自己的理解大概可以分为以下三类:
- 特殊字符:比如空格,制表符,回车,换行等。
- 限定字符:指定正则表达式的一个给定组件必须要出现多少次才能满足匹。
- 定位符:描述字符串或单词的边界。
特殊字符
特殊字符 | 意义 | 怎么记忆 |
---|---|---|
\n | 匹配一个换行符 | new line |
\f | 匹配一个换页符 | form feed |
\r | 匹配一个回车符 | retrun |
\s | 匹配一个空格符 | space |
\d | 匹配一个数字 | digit |
\w | 匹配一个包括下划线在内的任意字符 | word |
\t | 匹配一个制表符tab | tab |
\v | 匹配一个垂直制表符 | vertical tab |
[\b] | 匹配一个回退符 | backspace,使用[]符号是避免和\b重复 |
. | 匹配除换行符 \n 之外的任何单字符 | 点生万物 |
[ | 标记一个中括号表达式的开始。 | 就是括号的意思,小括号已经被子表达式占了 |
\ | 常用于转义,当需要匹配的字符属于元字符时,就需要转义 | 转义 |
I | 用于表示正则表达式的逻辑或 | 逻辑或 |
限定字符
字符 | 意义 | 怎么记忆 |
---|---|---|
* | 匹配前面的子表达式零次或多次。例如,zo* 能匹配 “z” 以及 “zoo”。* 等价于{0,}。 | 宇宙洪荒,辰宿列张:宇宙伊始,从无到有,最后星宿布满星空 |
+ | 匹配前面的子表达式一次或多次。例如,‘zo+’ 能匹配 “zo” 以及 “zoo”,但不能匹配 “z”。+ 等价于 {1,}。 | 1+加一 |
? | 匹配前面的子表达式零次或一次。例如,“do(es)?” 可以匹配 “do” 、 “does” 中的 “does” 、 “doxy” 中的 “do” 。? 等价于 {0,1}。 | 问号,有还是无? |
{n} | n 是一个非负整数。匹配确定的 n 次。例如,‘o{2}’ 不能匹配 “Bob” 中的 ‘o’,但是能匹配 “food” 中的两个 o。 | 可以想象成一个数轴,从一个点,到一个射线再到线段。n和m分别表示了左闭右闭区间的左界和右界 |
{n,} | n 是一个非负整数。至少匹配n 次。例如,‘o{2,}’ 不能匹配 “Bob” 中的 ‘o’,但能匹配 “foooood” 中的所有 o。‘o{1,}’ 等价于 ‘o+’。‘o{0,}’ 则等价于 ‘o*’。 | |
{n,m} | m 和 n 均为非负整数,其中n <= m。最少匹配 n 次且最多匹配 m 次。例如,“o{1,3}” 将匹配 “fooooood” 中的前三个 o。‘o{0,1}’ 等价于 ‘o?’。请注意在逗号和两个数之间不能有空格。 |
定位符
字符 | 意义 | 怎么记忆 |
---|---|---|
\b | 单词的开始或结尾 | bianjie |
\B | 非单词的开始或结尾 | 大写表示造反 |
^ | 待匹配的字符串的开头 | 小荷才露尖尖角 |
$ | 待匹配的字符串的结尾 | 有点终结者的感觉 |
示例分析
看完了基础常用的元字符,让我们来搞一波分析,加深记忆
- 例一:匹配正整数
^[1-9]\d*$
^ 和 $ 分别代表匹配字符串的开头和结尾,这个没的说,[1-9]\d表示匹配数字1到9,*表示0次或任意次。
- 例二:电话号码,例如(010)88886666,或022-22334455
\(?0\d{2}[) -]?\d{8}
=(?0\d{2}= 先把左括号(转义,它能出现一次或零次,然后是一个0,在后面是两个数字。==[) -]?==表示零次或一次的 “)-”,后面是八个数字
高段位用法
分组(子表达式)
所有以(和)元字符所包含的正则表达式被分为一组,每一个分组都是一个子表达式,它也是构成高级正则表达式的基础。如果只是使用简单的(regex)匹配语法本质上和不分组是一样的,如果要发挥它强大的作用,往往要结合回溯引用的方式。
回溯引用
所谓回溯引用(backreference)指的是模式的后面部分引用前面已经匹配到的子字符串。你可以把它想象成是变量,回溯引用的语法像\1,\2,…,其中\1表示引用的第一个子表达式,\2表示引用的第二个子表达式,以此类推。而\0则表示整个表达式。一个很经典的例子:
查找文本中两个相同的相邻单词
I am am a happy coder coder
可以用下面的正则表达式来匹配它
\b([a-z]+)\s\1\b
开头和结尾的 元字符限定的连续单词的开头和结尾,子表达式==(a-z+)==表示匹配一个或多个小写字母,\s表示一个空格符,\1 表示引用第一个子表达式,匹配的结果是 “ am am coder coder”
前向查找
前向查找(lookahead)是用来限制后缀的。凡是以(?=regex)包含的子表达式在匹配过程中都会用来限制前面的表达式的匹配。例如happy happily这两个单词,我想获得以happ开头的副词,那么就可以使用happ(?=ily)来匹配。如果我想过滤所有以happ开头的副词,那么也可以采用负前向查找的正则happ(?!ily),就会匹配到happy单词的happ前缀。
后向查找
介绍完前向查找,接着我们再来介绍一下它的反向操作:后向查找(lookbehind)。后向查找(lookbehind)是通过指定一个子表达式,然后从符合这个子表达式的位置出发开始查找符合规则的字串。举个简单的例子: apple和people都包含ple这个后缀,那么如果我只想找到apple的ple,该怎么做呢?我们可以通过限制app这个前缀,就能唯一确定ple这个单词了。
/(?<=app)ple/
其中(?<=regex)的语法就是我们这里要介绍的后向查找。regex指代的子表达式会作为限制项进行匹配,匹配到这个子表达式后,就会继续向后查找。另外一种限制匹配是利用(?<!regex) 语法,这里称为负后向查找。与正前向查找不同的是,被指定的子表达式不能被匹配到。于是,在上面的例子中,如果想要查找apple的ple也可以这么写成
/(?<!peo)ple。