资讯专栏INFORMATION COLUMN

用一杯咖啡的时间学会正则表达式

entner / 1043人阅读

摘要:如字段,使用,会匹配出使用,会匹配出元字符在正则表达式中有一些具有特殊含义的字母,被称为元字符,简言之,元字符就是描述字符的字符,它用于对字符表达式的内容转换及各种操作信息进行描述。

关于正则表达式的教程网上实在太多,但都不太友好,在此我整理一篇,面向自己行文。

学正则需要记语法,最好的方式无非就是实操,边写边学,因此第一步,推荐一个在线工具:

在线工具

regex101

正则的在线工具只推荐regex101,解释正则表达式、显示匹配信息、提供常用语法参考等,足够强大和界面友好。

基础知识

下面开始正式内容,在 Javascript 中,一个正则表达式以 / 开头和结尾,所以 /hello regexp/ 就是一个正则表达式。

正则中有几个大类需要了解:Flags、Character Sets、Quantifiers、Metacharacters、Special Characters、Groups、Assertion,下面我们一一来讲解。

Flags(标志符或修饰符)

Flags 写在结束的/之后,可以影响整个正则表达式的匹配行为。常见的 flags 有:

g:全局匹配(global);正则表达式默认只会返回第一个匹配结果,使用标志符 g 则可以返回所有匹配。

i:忽略大小写(case-insensitive);在匹配时忽略英文字母的大小写。

m:多行匹配(multiline);将开始和结束字符(^和$)视为在多行上工作,即分别匹配每一行(由 n 或 r 分割)的开始和结束,而不只是只匹配整个输入字符串的最开始和最末尾处。

所有的 flags 都打上的话,那就是/^*$/gimsuy

Character Sets(字符集合)

用于匹配字符集合中的任意一个字符。

[xyz]:匹配 xyz

[^xyz]:补集,匹配除了 x y z的其他字符。

[a-z]:匹配从 az 的任意字符。

[^a-n]:补集,匹配除了 an 的其他字符。

[A-Z]:匹配从 AZ 的任意字符。

[0-9]:匹配从 09 的任意数字。

匹配所有的字母和数字可写成:/[a-zA-Z0-9]/ 或者 /[a-z0-9]/i

Quantifiers (量词)

使用 Quantifiers 来实现重复匹配。

{n}:匹配 n 次。

{n,m}:匹配 n到m 次。

{n,}:匹配 >=n 次,即大于n次。

?:匹配 0 || 1 次,即 0或者1 次。

*:匹配 >=0 次,等价于 {0,}

+:匹配 >=1 次,等价于 {1,}

如字段abczzzzzzzxyabcabcabc,使用/z*xy/gi,会匹配出zzzzzzzxy

使用/z{0,2}xy/gi,会匹配出zzxy

Metacharacters(元字符)

在正则表达式中有一些具有特殊含义的字母,被称为元字符,简言之,元字符就是描述字符的字符,它用于对字符表达式的内容、转换及各种操作信息进行描述。

常见的元字符有:

d:匹配任意数字,等价于 [0-9]

D:匹配任意非数字字符;d 的补集。

w:匹配任意基本拉丁字母表中的字母和数字,以及下划线;等价于 [A-Za-z0-9_]

W:匹配任意非基本拉丁字母表中的字母和数字,以及下划线;w 的补集。

s:匹配一个空白符,包括空格、制表符、换页符、换行符和其他 Unicode 空格。

S:匹配一个非空白符;s 的补集。

:匹配一个零宽单词边界,如一个字母与一个空格之间;例如,/no/匹配 at noon 中的 no/ly/ 匹配 possibly yesterday. 中的 ly

B:匹配一个零宽非单词边界,如两个字母之间或两个空格之间;例如,/Bon/ 匹配 at noon 中的 on/yeB/ 匹配 possibly yesterday. 中的 ye

:匹配一个水平制表符(tab)。

:匹配一个换行符(newline)。

:匹配一个回车符(carriage return)。

理解 零宽单词边界

我们看以下的匹配:

上图中,紫红色竖线表示的位置就是零宽单词边界(Matches a zero-width word boundary),说白了,就是一个字母的两侧是否有空格,我们输入内容,就知道匹配的是什么了:

Special Characters (特殊字符)

正则中存在一些特殊字符,它们不会按照字面意思进行匹配,而有特殊的意义,比如前文讲过用于量词的?*+

其他常见的特殊字符有:

:转义字符,可以将普通字符转成特殊字符。比如w;也可以将特殊字符转成字面意思,比如 + 匹配 +

.:匹配任意单个字符,但是换行符除外: , , u2028u2029;在字符集中[.],无特殊含义,即表示 . 的字面意思。

|:替换字符(alternate character),匹配 | 前或后的表达式。比如需要同时匹配 bearpear,可以使用 /(b|p)ear/ 或者 /bear|pear/;但是不能用 /b|pear/,该表达式只能匹配 bpear

^:匹配输入的开始。比如,/^A/ 不匹配 an Apple 中的 A,但匹配 An apple 中的 A

$:匹配输入的结尾。比如,/t$/ 不匹配 eater 中的 t,但匹配 eat 中的 t

^$ 在表单验证时常需要使用,因为需要验证从开始到结尾的一个完整输入,而不是匹配输入中的某一段。

Groups(分组)

(xyz):捕获分组(Capturing Group),匹配并捕获匹配项;例如,/(foo)/ 匹配且捕获 foo bar. 中的 foo

被匹配的子字符串可以在结果数组的元素 [1], ..., [n] 中找到,或在被定义的 RegExp 对象的属性 $1, ..., $9 中找到。

(?:xyz):非捕获分组(Non-capturing Group),匹配但不会捕获匹配项;匹配项不能再次被访问到。

:n 是一个正整数,表示反向引用(back reference),指向正则表达式中第 n 个括号(从左开始数)中匹配的子字符串;例如,/apple(,)sorange1/ 匹配 apple, orange, cherry, peach. 中的 apple, orange,

我们来理解一下:“非捕获分组(Non-capturing Group)”是什么意思?

先执行如下代码:

var string = "at noonfooasfodsoo";
var regex = /(foo)/gi;
var c1 = regex.exec(string);
var regex1 = /(?:foo)/gi;
var c2 = regex1.exec(string);

看看输出结果c1c2值的区别:

返回的数组中少了[1]的内容,我们到 regex101 上去也看看区别:

所以使用(?:xyz),意思是匹配并但不会捕获匹配项到 Group 中。

我们再来理解什么是“反向引用(back reference)”

如上例,/apple(,)sorange1/中的1指的其实就是(,),这个表达式等同的写法是/apple(,)sorange(,)/

我们试试2,也就是等价于左数第二个括号中的内容,看看匹配到的内容,我们在文本内容中再多加一个,

Assertion(断言)

x(?=y):仅匹配被 y 跟随的 x,这个也被称为:“先行断言”/“正向肯定查找”

例如,/bruce(?=wayne)/,如果bruce后面跟着wayne,则匹配bruce

/bruce(?=wayne|banner)/,如果bruce后面跟着wayne或者banner,则匹配bruce

waynebanner 都不会在匹配结果中出现。

(?<=y)x:匹配"x"仅当"x"前面是"y",这个也被称为:“后行断言”

例如,(?<=bruce)wayne,如果wayne前面是bruce,则匹配wayne

/(?<=bruce|banner)wayne/,如果wayne前面是bruce或者banner,则匹配wayne

waynebanner 都不会在匹配结果中出现。

x(?!y):仅匹配不被 y 跟随的 x,这个也被称为:“正向否定查找”

例如,/d+(?!.)/ 只会匹配不被 "." 跟随的数字。

/d+(?!.)/.exec("3.141") 匹配 141,而不是 3.141

非贪婪匹配(non-greedy)

默认情况下,正则表达式的量词*+{},都是进行贪婪匹配(greedy),即匹配尽可能多的字符。

非贪婪匹配,即匹配尽量少的字符(matching the fewest possible characters)。

怎么理解呢?
例如这么一段话:The reading of all good books is like a conversation with the finest men of past centuries.

使用/.+s/匹配,.可以匹配任意字符,而+表示匹配 1 次或者多次,默认是贪婪匹配,所以会匹配到了最后一个空格符才结束。

而当我们在量词*+{}后面紧跟着一个?,就可以实现非贪婪匹配。

例如/.+?s/,匹配到第一个空格符就会结束:

多行匹配

例如下面一段文字:

May God bless and keep you always,
may your wishes all come true,
may you always do for others
and let others do for you.
may you build a ladder to the stars
and climb on every rung,
may you stay forever young,
forever young, forever young,
May you stay forever young.

如何匹配以 forever 开头的那句歌词forever young, forever young呢?

/^forever.+/是错误的,因为^匹配的整个字符串的开始,而是不是每一行的开始。

所以指定m选项,即可支持多行匹配,这时^$匹配的是每一行的开始和结束,因此正确的正则表达式是/^forever.+/m

应用例子 1. 匹配手机号码

匹配1(3/4/5/7/8)开头的11位数字

以 1 开头:/^1/

第 2 位为 3、4、5、7、8 中的一个:/[34578]//(3|4|5|7|8)/

剩余 3-11 位均为数字,并以数字结尾:/d{9}$/

组合起来即为 /^1[34578]d{9}$//^1(3|4|5|7|8)d{9}$/,因为使用捕获括号存在性能损失,所以推荐使用第一种写法。

2. 匹配电子邮件

标准的电子邮件组成为 @.

每部分的格式标准为:

yourname:任意英文字母(a-z/A-Z)、数字(0-9)、下划线(_)、英文句点(.)、连字符(-),长度大于 0

domain:任意英文字母(a-z/A-Z)、数字(0-9)、连字符(-),长度大于 0

extension:任意英文字母(a-z/A-Z),长度 2-8

optional-extension:"."开头,后面跟任意英文字母(a-z/A-Z),长度 2-8,可选

每部分的正则表达式为:

yourname/[a-zd._-]+/

domain/[a-zd-]+/

extension/[a-z]{2,8}/

optional-extension/(.[a-z]{2,8})?/

组合起来形成最后的正则表达式:/^([a-zd._-]+)@([a-zd-]+).([a-z]{2,8})(.[a-z]{2,8})?$/ig;为了增加可读性可以将每部分用()包起来,并不要忘记起始和结束符^$

掌握上面所说的这些内容,日常使用的正则基本不会有什么问题了。

参考文章

MDN

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/105004.html

相关文章

  • JS正则达式学习笔记1

    摘要:正则表达式作为前端学习的一个知识点,是每个合格的前端开发都应该掌握它的用法。元字符一般情况下,正则表达式的一个字符对应字符串的一个字符。 正则表达式作为前端学习的一个知识点,是每个合格的前端开发都应该掌握它的用法。正则表达式的学习确实不难,语法和应用也非常简单,能够快速入门,很轻松的就能写出简单的表达式来对字符串执行某些操作。网上也有标题党说一杯咖啡的时间就能学会。能学会吗?能!但要真...

    wangdai 评论0 收藏0
  • 模板方法模式

    摘要:嗯,模板模式应该是跟杨洋一样帅,所以带着这份爱慕,我们一起来看看它到底有没有比杨洋还要帅模板方法模式是什么模板方法模式是一种只需使用继承就可以实现的非常简单的模式。模板方法模式由两部分结构组成,第一部分是抽象父类,第二部分是具体实现的子类。 JavaScript-模板方法模式 模板方法是什么鬼?模板模式又是什么鬼?? 听说它很复杂,听说它很难,我可不可以不学啊?。刷了一会儿微博,这几天...

    Scorpion 评论0 收藏0
  • 开发之路(设计模式三:装饰者模式)

    摘要:若要扩展功能,装饰者提供了比继承更有弹性的替代方案。装饰者模式意味着一群装饰者类,这些类用来包装具体组件。装饰者类反映出被装饰组件类型。装饰者会导致设计中出现许多小对象,如果过度使用,会让程序变得很复杂。 嘿嘿嘿,你是不是很喜欢用继承呢?感觉没什么事情是一个爸爸类搞不定的,有的话就两个,快来跟我看看这个模式吧,它能让你断奶,给爱用继承的人一个全新的设计眼界。 直奔主题,你是否有听说...

    Vicky 评论0 收藏0

发表评论

0条评论

entner

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<