学习正则的心得

less than 1 minute read

不得不说学习正则后,工作效率或多或少有提升。正则给我最直观的感受就是处理字符串的时候得心应手、变化莫测。下图很形象地反映出开发人员对正则的态度:

正则是一种概念,不同的程序设计语言有不同的实现,但功能大同小异,这里要说的是 JavaScript 里的正则表达式。

推荐

大多数人都是通过网上的资料学习正则,但都比较零散,久而久之会混淆我们的记忆,所以系统学习正则效果会比较好。

这里推荐一本电子书:JavaScript 正则表达式迷你书,作者是老姚

在线测试工具推荐两款:RegulexRegEx Pal

还有一个大而全的正则学习网站:Regular-Expressions.info

例子

下面举几个工作中遇到的例子:

邮箱匹配

这是个很常见的匹配操作,一般邮箱格式是:登录名@主机名.域名。

其中,登录名一般包含数字、大小写字母、下划线,即 \w+,主机名和域名一般包含数字、大小写字母,即 [0-9a-zA-Z]+

那么正则表达式可以写成:/^\w+@[0-9a-zA-Z]+(\.[0-9a-zA-Z]+)+$/g,当然还有其它的写法,具体看业务需求。

匹配手机号

匹配手机号也是有很多种写法,我们可能会想到这种:/^(18[0-9]{9})|(15[0-9]{9})$/

虽然这条正则能匹配手机号,但是有点问题,就是它也能匹配 18444444444444444444。

原因很简单,这个表达式相当于:/(^18[0-9]{9})|(15[0-9]{9}$)/。因为 ^ 的优先级比 | 高,所以 ^ 先与 18[0-9]{9} 匹配了,$ 同理。

关系操作的正则

像大于号、小于号等这些关系操作的正则,并没有直接的操作符可以表示,但可以用“或”操作符间接解决。例如,这个表达式可匹配小于等于 90:/^[0-8]?[0-9]|90$/g

使用逗号分隔数字

在处理一些外国数字格式时,需要每三位就加一个逗号,比如 12345678 如何变成 12,345,678?

了解“位置”的概念帮助我们解决这个问题。

位置的理解:

  1. 匹配位置的6个锚字符:^ $ \b \B (?=p) (?!p)
  2. 位置不占宽度(零宽),所以对于位置可以理解成空字符
  3. 位置可以替换为字符

所以思路是把位置替换为逗号:/(?!\b)(?=\d{3}+\b)/,其中 \b 表示位置,(?!\b) 则保证开头不会出现逗号。

\b 和 \B 的区别:

  1. \b是单词边界,具体就是\w和\W之间的位置,也包括^和\w之间的位置,也包括\w和$之间的位置
  2. \B就是\b的反面的意思,非单词边界。例如在字符串中所有位置中,扣掉\b,剩下的都是\B的。

匹配简单标签

匹配简单标签(不含属性)包含的内容时,可能会想到“前瞻后顾”,由于 JavaScript 正则目前支持只前膽不支持后顾,可以换另一种写法:/<([_a-zA-Z][^>]*)>(.*)<\/\1>/

JavaScript 正则不支持的语法有:

  1. 锚位符:\Aexp\Z(Perl),可以用^exp$
  2. 匹配字符串字面值:\Q需反义字符\E
  3. 反向引用:(exp)$1(sed、Perl),可以用(exp)\1
  4. 命名分组
  5. 并集:[exp[exp]](Java)
  6. 差集:[exp&&[exp]](Java)
  7. 正反后顾:(?<=exp)exp(?<!exp)exp,可能在 ES 未来版本中会支持

反向引用

上个例子出现的 \1 是对前文括号内的引用,类似的,String.replace 则是用 $1 来引用前文。

例如如何将 [a]123[/a] 替换为 <a>123</a>

像匹配这种相同内容的,我们可以考虑“反向引用”:'[a]123[/a]'.replace(/\[([^\[\]]*?)\]/g,'<$1>')

反向引用还有一个特点:

  1. 如果非后顾断言 /(o)d\1/,引用 \1 放在捕获组 (o) 后
  2. 如果是后顾断言 /(?<=\1d(o))/,引用 \1 放在捕获组 (o) 前

匹配某个单词

有个题目是将 HTML 标签里的属性去掉,除了 src。

匹配一个单词可以用正向前瞻来匹配:/(?=word).*/g

所以我们可以用反向前瞻来排除 src:/( (?!src=)([a-z]*)="(.|\s)*?")/g

Updated:

Leave a comment