学习正则的心得
不得不说学习正则后,工作效率或多或少有提升。正则给我最直观的感受就是处理字符串的时候得心应手、变化莫测。下图很形象地反映出开发人员对正则的态度:
正则是一种概念,不同的程序设计语言有不同的实现,但功能大同小异,这里要说的是 JavaScript 里的正则表达式。
推荐
大多数人都是通过网上的资料学习正则,但都比较零散,久而久之会混淆我们的记忆,所以系统学习正则效果会比较好。
这里推荐一本电子书:JavaScript 正则表达式迷你书,作者是老姚
还有一个大而全的正则学习网站: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?
了解“位置”的概念帮助我们解决这个问题。
位置的理解:
- 匹配位置的6个锚字符:^ $ \b \B (?=p) (?!p)
- 位置不占宽度(零宽),所以对于位置可以理解成空字符
- 位置可以替换为字符
所以思路是把位置替换为逗号:/(?!\b)(?=\d{3}+\b)/
,其中 \b
表示位置,(?!\b)
则保证开头不会出现逗号。
\b 和 \B 的区别:
- \b是单词边界,具体就是\w和\W之间的位置,也包括^和\w之间的位置,也包括\w和$之间的位置
- \B就是\b的反面的意思,非单词边界。例如在字符串中所有位置中,扣掉\b,剩下的都是\B的。
匹配简单标签
匹配简单标签(不含属性)包含的内容时,可能会想到“前瞻后顾”,由于 JavaScript 正则目前支持只前膽不支持后顾,可以换另一种写法:/<([_a-zA-Z][^>]*)>(.*)<\/\1>/
。
JavaScript 正则不支持的语法有:
- 锚位符:
\Aexp\Z
(Perl),可以用^exp$
- 匹配字符串字面值:
\Q需反义字符\E
- 反向引用:
(exp)$1
(sed、Perl),可以用(exp)\1
- 命名分组
- 并集:
[exp[exp]]
(Java) - 差集:
[exp&&[exp]]
(Java) - 正反后顾:
(?<=exp)exp
、(?<!exp)exp
,可能在 ES 未来版本中会支持
反向引用
上个例子出现的 \1
是对前文括号内的引用,类似的,String.replace 则是用 $1
来引用前文。
例如如何将 [a]123[/a]
替换为 <a>123</a>
?
像匹配这种相同内容的,我们可以考虑“反向引用”:'[a]123[/a]'.replace(/\[([^\[\]]*?)\]/g,'<$1>')
。
反向引用还有一个特点:
- 如果非后顾断言 /(o)d\1/,引用 \1 放在捕获组 (o) 后
- 如果是后顾断言 /(?<=\1d(o))/,引用 \1 放在捕获组 (o) 前
匹配某个单词
有个题目是将 HTML 标签里的属性去掉,除了 src。
匹配一个单词可以用正向前瞻来匹配:/(?=word).*/g
。
所以我们可以用反向前瞻来排除 src:/( (?!src=)([a-z]*)="(.|\s)*?")/g
。
Leave a comment