Regular Expressions

基本格式与匹配范围

基本格式为/[regx]/[g\i\m\s\u\y]。其中[g\i\m\s\u\y]每个字母表示一种匹配范围,可同时出现:

/g:输出所有匹配结果;
/i:不区分大小写;
/m^$定位符在场时才发挥作用;
/:只输出第一个匹配结果。

Regexr Website是一个很好的练习正则表达式的网站,它的界面简洁:上方是正则表达式,中间是待匹配文本,下方则会详细给出每个表达式的匹配细节同时还提供了一些实用工具。

1

Fig. 1. Regexr Website

运算规则与优先级

所有的正则表达式,无论是基本运算符还是复杂运算符,都遵循优先级从左到右匹配。基本的优先级从高到低顺序为:

运算符 描述
\\ 转义字符
(), [] 圆括号,方括号
*, +, ?, {} 限定符
^, $, \w等元字符, 任何字符 定位点和字符
| 逻辑运算符,或

对于待匹配字符串,也遵循从左到右、从上到下的匹配顺序。因此,一旦某子串符合正则表示式的匹配要求,该子串便会立即被“摘出”字符串,不再参与后续的匹配。


简单的运算符

  • +:对+的前一个字符匹配1个以上;
  • ?:对?的前一个字符匹配0或1个;
  • *:对*的前一个字符匹配0或多个;
  • .:匹配任何1个字符(除换行符);
  • \:转义字符,使运算符失去正则意义;
  • \d:匹配数字;
  • \w:匹配单词;
  • \s:匹配空白符,包括space、tab、line break。

\w等的大写表示匹配与原小写相反的项目,如\W匹配所有的非单词,实际相当于对小写的匹配结果取非。


较复杂的运算符

括号类

  • {[minLength],[maxLength]}:对{[minLength],[maxLength]}前的所有匹配项,限制每个匹配项的长度为minLength-maxLength,其中匹配项长度小于[minLength]的将会被剔除,而长度大于[maxLength]的则会只取[maxLength]部分。如{4,5}只会匹配其之前的匹配项中所有长度大于4的,并对大于5的限定长度为5,限定长度后的剩下部分若仍符合前面的匹配要求,则当作新的匹配项继续参与{[minLength],[maxLength]}。若只有{[minLength]}则默认[maxLength]=[minLength];若是{[minLength],}则将匹配所有长度大于[minLength]的;
  • [[Groups]]:匹配任何一个[[Groups]]中的字符。这很像通配符中[]的用法,但是不同的是,正则表达式中没有组的概念,因此[Groups]只能是用户自定的一组备选的字符或序列,如[abg]将匹配abga-f将匹配af的任意一个字符;1-9将匹配19的任意一个字符。[]支持组合使用,如a-f1-9将匹配af19的任意一个字符;
  • ()()表将里面的内容视作一个整体,即里面的内容在匹配时实际上是一个字符,如(re){2,3}将匹配所有的re重复超过2次的连续串中的rerererere部分,此时re整体被视为一个字符;

逻辑运算符与定位符

  • |:逻辑运算符or,匹配结果是符合|左边或右边内容的字符串。若无括号将整个|表达式括起,|会默认将左边和右边的视为一个整体,因为|的优先级最低,如tre|it只会匹配treit,而t(re|it)则会匹配'tretit
  • ^:需放在正则表达式的开头才起作用,表示匹配字符串的开头,也就是说^后面的表达式只有出现在字符串的开头才会被匹配。默认情况下,整篇文章会被视作一个字符串,但是若使用/m选项,则每一行会被视作一个字符串,此时^的意义就是匹配每一行的开头;
  • $:需放在正则表达式的最后才起作用,类似于^,表匹配文章或行的末尾;
  • ?<=:向前看,一般而言,这个表达式必须和括号()一起使用。它是^的拓展版,表示从以括号内的表达式开头的部分开始匹配,但是不匹配括号内的内容,如(?<=[tT]he).将匹配theThe后面的任意一个字符;
  • ?<!?<=的取反,如(?<![tT]he).将匹配除(?<=[tT]he).匹配结果的所有字符;
  • ?=:向后看,?<=的倒装版,是$的拓展,表示从以括号内的表达式结尾的部分开始匹配,如.(?=[tT]he)将匹配theThe前面的任意一个字符;
  • ?!?=的取反。

需要注意的是,并不是所有的正则表达式标准都包含了?<=?<!


实战-电话号码匹配

对于一般的11位电话号码,其最常规的写法有如下这三种:

1
2
3
1234567890
123-456-7890
123 456 7890

不难看出,上面的写法都可以统一成334写法,因此我们可以先按334将电话号码分组:

1
/(\d{3})(\d{3})(\d{4})/g

三者的区别就在于 -以及无字符,对于 -,我们可以使用中括号表任选其一[ -],而对于无字符则可以采用?来匹配前方的0或1个字符:

1
/(\d{3})[ -]?(\d{3})[ -]?(\d{4})/g

上述式子已经可以匹配所有的这3种写法。有时,还会出现加了国际区号和使用括号的写法,如:

1
2
(123) 456-7890
+1 123 456 7890

对于加括号的,用\(?\)?处理即可。对于带区号的,则可以(\+\d )?处理。所有的合起来就是:

1
/(\+\d )?(\(?\d{3}\)?)[ -]?(\d{3})[ -]?(\d{4})/g

参考