正则表达式入门八要

正则表达式对应的英文是 Regular Expression, 直译为规则而整齐的表达式。
本文所讲8个要点基本涵盖了正则知识点所有常用知识点。

正则是一个汉语词汇,基本意思是正其礼仪法则;正规;常规;正宗等。出自《楚辞·离骚》、《插图本中国文学史》、《东京赋》等文献。

不要纠结名字,就是一种表达式写法的命名而已。你就粗暴的认为是个叫“正则”的人发明的表达式吧。
推荐使用在线正则验证工具RegexR练习使用正则,切换至PCRE Engine(支持的正则语法更全)

要点1: 单字符的匹配

一“点”代所有

.可匹配所有字符(注:在多行模式下,不能匹配换行符)

转义即所见

在正则表达式中,既然.能表示所有字符,那么只想匹配“点”字符呢? 只需在“点”前面加上\ 符号即可,这个符号就被命名为转义字符了。
如果想匹配\怎么办? 前面再加一个转义字符就是了: \\

中括涵范围

中括号中写上字符范围、或一系列指定字符,那么将只匹配这个范围内的字符。

  • 比如 [1234567890]只匹配数字, [abcACB]只匹配字母a、b、c, A、B、C
  • 匹配一个范围还可以这么写 [0-9], 匹配所有英文字母 [a-zA-Z]

注意到,上面定义范围时使用了- ,它是严格按照ASCII码表的前后顺序定义的,只能是a-z, 而不能是z-a, 附ACSII码截图

要点2: 字符集缩写

预热: 先看几个英文单词

  1. digit
  2. word
  3. space

正题

  • \d 是字符范围 [0-9] 的缩写
  • \w 是字符范围 [0-9a-zA-Z_]的缩写
  • \s 表示一个空格

他们的对立面–将字母换成大写

  • \D 非数字
  • \W 非文本
  • \S 非空格

要点3: 数量限制

  • 0个或1个: ?
  • 1个或多个: +
  • 0个或多个:*
  • m个到n个: {m, n}
  • m个: {m}

应用举例:
实验文本: aaab

  • a?b 返回匹配结果 ab
  • a+b 返回匹配结果 aaab
  • a*b 返回匹配结果 aaab
  • a{1,2}b 返回匹配结果 aab
  • a{2}b 返回匹配结果 aab

思考
从上述实例,为什么匹配时虽加了数量限制,但是确始终返回最多的匹配结果? 比如,+表示1个或多个,在a+b中没有返回ab,而是直接返回了3个a的aaab

贪婪与非贪婪

  • 默认是贪婪的,匹配数量时尽可能多。
  • 若在量词后添加?修饰符,则变成 非 贪婪(也可以叫懒惰),匹配数量时尽可能少
  • 举例: 针对文本<a href="xxxx">ezmo</a>, 使用<.*>匹配的话是所有文本, 是用<.*?>只匹配了前后两个a标签。

要点4: 文本边界

始于奋斗(向上冲),死于金钱

  • 一行的开头 ^
  • 一行的结果 $

高级

  • \w\W 之间的 不可知地(出自小说《将夜》,看不到摸不着,但确实存在的地方) ——- \b
    思考: 对于文本 “chenjihu@gmail_gmail.com”,我只想匹配第一个gmail,该如何做?

扩展知识点

  • 单行模式: 多行文本当成一行,只有一个开头和结尾
  • 多行模式:多行文本就是多行,每行都有一个开头和结尾

要点5: 分组、或

场景:
有文本 ”win10 is windows, win9 is windows”, 想要一步将其中的win10和win9中的win替换成 windows+空格,如何做?

  • 正则里用 小括号给要匹配的文本扩上,便于引用和替换,这就是分组
  • 上例中 使用 win\d+ 可以匹配上win10和win9,想要保留 10和9,替换掉前面的win,可以将数字部分放在一个组内,替换时这个组不动,组外的部分设为空字符即可。
  • win(\d+) 匹配后使用 $1替换,win被替换没了,$1表示第一个组内的内容。

高级用法-反向引用
还是使用同一个场景,需要匹配出文本 “win10 is win” 和“win9 is win”

发现规律-匹配出的文本前后都有win

  • 可以这样写: win.*?win win.+?win
  • 也可以使用反向引用(win).*?\1 ,其中的\1 代指前面的第一个分组内容

|的用法
还是前述字符串,只想替换win9、windows 为winXP,怎么做?
答案: (win9|windows) 匹配后,替换文本为 winXP

要点6: 正向预查和负向预查

文本 ”win10 is windows, win9 is windows”, 想将其中所有的win10,win9中的数字替换为XX。
可以使用前面所介绍的分组特性来做: (win)\d+ 替换为 $1XX

  • 正向预查: win(?=10) 查出所有其后为10的win
    win(?!=10) 查出所有其后不为10的win
  • 负向预查: (?<=win)10 查出所有数字10,其前面必须是win。( Javascript不支持这个语法) 实现前例同样功能的表达式,使用 (?:win)\d+ 匹配后,用 XX替换即可, 请自行比较不同。
    (?<!win)10查出所有数字10,其前面不能是win

要点7: 非捕获分组

非捕获分组的语法是 (?:不会创建组)
所谓的非捕获,非捕获数组不参与组编号分配但参与匹配,即匹配时不会为它创建一个分组,替换时也无法使用 $1,$2等引用它。

举例, 文本“win10 is windows”
普通捕获分组:采用(win)10 匹配后,可用 $1xp替换为 “winxp is windows”
非捕获分组: 采用(?:win)10 匹配后,无法使用 $1引用到win,因为它不参与组的编号分配。

要点8: if else (高级,可选)

(abc)?de(?(1)f|w) 若匹配上abc,则de后匹配f,否则匹配w
对于字符串 “abcdew”, 匹配出的结果是dew , 对于字符串“abcdefw” 匹配出的结果是 abcdef