打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
正则表达式基础

今天在 segmentfault 回答了一个关于正则表达式的问题,后来细想下,觉得自己对正则表达式的知识其实也不是很熟悉,很多时候都是要用的时候查查找找搞定的,因此我决定把一些关于正则表达式的知识系统地学习和总结下。
说明:为了书写方便,本文中 => 表示匹配,!=> 表示不匹配。

字符集

. 作为一个通配符可以代替除换行符(\n)外的任一一个字符。

'.ig' => 'big' 和 'pig'

[] 包含在方括号中的内容组成一个字符集合,但是只能匹配其中的一个字符。

 '[a-z]b' => 'sb', '[a-z]b'  !=> '2b'

[] 内可以使用脱字符号 ^ 来指明不匹配某个字符集合。

'[^a-z]b' => '2b', '[^a-z]b' !=> 'sb'

字符类

这些字符类都是正则预定义的:

  • \f 换页符
  • \n 换行符
  • \r 回车符
  • \t 制表符
  • \v 垂直制表符
  • \s 任何空白字符,包括空格、制表符、换页符等。相当于[\f\n\r\t\v]
  • \S 任何非空白字符。相当于[^\f\r\t\v]
  • \w 任何单词字符以及下划线。相当于[A-Za-z0-9_]
  • \W 任何非单词字符以及下划线。相当于[^A-Za-z0-9_]
  • \d 任何数字字符。相当于[0-9]
  • \D 任何非数字字符。相当于[^0-9]

重复字符

* 模式可以重复0次或多次。

'ha*' => 'h', 'ha*' => 'haaaaaa'

+ 模式可以重复1次或多次。

'ha+' !=> 'h', 'ha+' => 'haaaaaa'

模式可以重复0次或1次。

'ha?' => 'h', 'ha?' => 'haaaaaa'

子表达式

() 用来表示子表达式。

'(very)good' => 'verygood'  '(very)*good' => 'veryverygood'

{} 可以用来给子表达式计数。

'(very){2}good' !=> 'verygood',  '(very){2}good' => 'veryverygood' 

还可以给计数设定范围

'(very){1,3}good' => 'verygood',  '(very){1,3}good' => 'veryverygood'

需要注意的是,当写成 {n,} 形式表示匹配 n 次或更多次。

字符定位

^ 脱字符号在此的作用与字符集内的作用不同,它表示匹配的内容在被搜索字符串的开始位置。

'^nb' => 'nba', '^nb' !=> 'wnba'

为了与上面的脱字字符使用进行区分,再举个例子:

'^nb' !=> 'wnba', '[^nb]' => 'wnba'

$^ 相反,它表示匹配的内容在被搜索字符串的末尾位置。

'b$' => 'nb', 'b$' !=> 'nba'

\b 有单词分界符,这个容易模糊,举个例子来理解:

'hello\b' => 'hello world', 'hello\b' => 'hello,world', 'hello\b' !=> 'helloworld'

\B 刚好相反,它表示无非单词分界符。

'hello\B' !=> 'hello world', 'hello\B'!=> 'hello,world', 'hello\B' => 'helloworld'

字符分支

| 表示子表达式的分支,分支内可选匹配。

'(cba)|(nba)' => 'cba', '(cba)|(nba)' => 'nba'

匹配特殊字符

在上面我们用到了很多特殊的字符来表达特定的意思,但是如果刚好就是想要匹配这些特殊字符本身,而并非是这些特殊字符所表达的意义。此时我们只需在字符前加一个反斜杠 \

'$ab' !=> '$abc' //这里我们只是希望匹配 $ab,而不是希望匹配出现在末尾的 ab。

所以我们需要添加反斜杠 \

'\$ab' => '$abc' //匹配成功

模式修正符

  • i 不区分大小写(默认是区分的)
  • m 在匹配首内容或者尾内容时候采用多行识别匹配
  • S 将转义回车取消
  • x 忽略正则中的空白
  • A 强制从头开始匹配
  • D 强制$匹配尾部无任何内容
  • U 禁止贪婪匹配 只跟踪到最近的一个匹配符并结束

贪婪匹配与懒惰匹配

贪婪匹配

当正则表达式中包含能接受重复的限定符 *+ 时,为了使表达式得到匹配,通常是匹配尽可能多的字符。这被称为贪婪匹配。

'ha.*' => 'haaaaaaaaaaha'//匹配的结果为最长的 haaaaaaaaaa
懒惰匹配

懒惰匹配,也就是匹配尽可能少的字符。此时只需要在重复限定符后加一个疑问号

'ha.*?' => 'ha'//匹配的结果为最少的 ha

断言和负断言

断言

先行断言

如果我们希望达到断言匹配结果后面一定跟着某个子表达式,比如我们希望匹配 singing 中的 sing,而不是 singer 中的 sing,因为我们先行断言 sing 后面的子表达式 ing,那么我们可以使用 (?=ing) 来实现。

'sing(?=ing)' => 'singing', 'sing(?=ing)' !=> 'singer'
后发断言

如果我们希望达到断言匹配结果前面一定跟着某个子表达式,比如我们希望匹配 nba 中的 ba,而不是 cba 中的 ba,因为我们后发断言 ba 后面的子表达式 n,那么我们可以使用 (?<=n)(?>n) 来实现。

'(?<=n)ba' => 'nba', '(?<=n)ba' !=> 'cba'

负断言

负先行断言

与先行断言相反,我们希望匹配结果后面一定不能跟着某个子表达式,比如,我们希望匹配不以 ing 结尾的 sing。那么我们可以使用 (?!ing) 来实现。

'sing(?!ing)' !=> 'singing', 'sing(?!ing)' => 'singer'
负后发断言

与后发断言相反,我们希望匹配结果前面一定不能跟着某个子表达式,比如,我们希望匹配不以 n 开始的 ba。那么我们可以使用 (?<!n) 来实现。

'(?<!n)ba' !=> 'nba', '(?<!n)ba' => 'cba'

固化分组

我们可以给匹配的子表达式进行固化分组,首先我们明确两点点:

  • 分组0对应整个正则表达式
  • 组号分配过程是要从左向右扫描两遍的:第一遍只给未命名组分配,第二遍只给命名组分配--因此所有命名组的组号都大于未命名的组号
    如我们希望对 i love you 内的 love 进行分组,可对其进行的操作包括:

    1.(love) //匹配 love,并自动归到未命名分组中
    2.(?<NAME>love) //匹配 love,并归到命名组中
    3.(?:love) //匹配 love,但不对其进行分组操作

举例说明,我们要匹配 lovelove,那么我们只需要匹配 love,然后利用它的组名即可。

'(love)\1' => 'lovelove', '(love)\1' !=> 'love' //未命名分组组名从1开始

这里要注意一点,如果我们需要匹配 love love,中间有单词分隔时,我们需要用到上文中的单词分解符 \b 和 空白符 \s

'\b(love)\b\s\1' => 'love love', '\b(love)\b\s\1' !=> 'lovelove'

匹配优先级

  1. () 字表达式因为是内存处理所以优先级最高
  2. + {} 重复匹配相关的优先级第二
  3. ^ $ \b 字符定位相关优先级第三
  4. 条件处理优先级第四
本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
正则表达式参考手册__Mini版
QRegExp正则表达式
python基础之正则表达式(十三)
Java正则表达式 预搜索(零宽断言)详解(精)
正则表达式 BRE 与 ERE的区别
grep断言【转】
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服