美文网首页
轻松入门正则表达式

轻松入门正则表达式

作者: ChrisZ_B612 | 来源:发表于2019-01-28 01:57 被阅读0次

    楔子

    为什么要写这篇文章?

    最近团队内有小伙伴在阅读vue的源代码,然后表示代码中有很多正则表达式的应用,而自己却对正则不太熟悉。后来我发现团队内其他的小伙伴也都表示正则表达式太难了,难学难用又难看懂。所以,正则表达式真的这么难吗?我的答案是:不是难,是真TM难。我自己这么些年也是把正则表达式前前后后学了起码五六遍才算是基本能熟练运用它。可是即便如此,我在准备这篇文章的过程中还是学到了一些新的东西,可见这玩意儿是有多么的反人类了。

    既然正则表达式这么难,那我们还要去认真学习它吗?

    是的,因为正则表达式功能十分强大,它能极大地提高我们的工作效率和开发效率,所以作为一名开发人员,我们必须熟练掌握它。

    那学习正则表达式有没有什么好的办法或者好的学习资料呢?

    当然有!不然这篇文章写来干嘛?话不多说,我们赶紧开始吧!

    本文目标

    我学习正则表达式的主要资源就是这篇非常有名的《正则表达式30分钟入门教程》,这篇文章针对正则表达式的知识点写得很详细很全面,我接下来要讲的主要知识点也都来源于它,不同的是,我会着重分享一些正则表达式的记忆技巧来帮助大家快速掌握这门技能。本文的目标是让你快速地、轻松地掌握正则表达式的基本使用,以便能快速应用到工作中去,所以我推荐你先阅读我的这篇快速入门文章,再去仔细研读《正则表达式30分钟入门教程》,以便达到最佳的学习效果。

    正文开始

    初识正则表达式

    简单来说,正则表达式就是一串用于描述文本规则的代码。举个例子,比方说你要在一段文本中搜索数字0,那么你可以直接使用正则表达式0(你没看错,这就是一个最简单的正则表达式),如果你想要搜索任意的数字,这时你可以使用或符号 | 来做到这一点,就像这样 0|1|2|3|4|5|6|7|8|9,略显繁琐?还有一种办法,就是使用方括号 [] 把你要搜索的字符括起来,就像这样 [0123456789],还是觉得麻烦?没问题,还有更简单的办法,就是使用 \d 搜索即可(d代表digit,即数字)。看到这里,其实你已经学习到了正则表达式的三个知识点了,是不是感觉也没那么难:)。分享一个我常用的网站:regex101,它可以让你在线测试正则表达式的效果(网站的左侧设置icon中可以设置中文界面),学习正则表达式一定要亲自测试,所以我强烈推荐你动手操作一把。

    元字符(meta character)

    你刚刚已经接触到过元字符\d了,正则表达式中还有一些其他的元字符,如下图所示:

    常用的元字符

    先看元字符\d,它匹配的是0到9的一个数字。再看元字符\w,这个厉害了!它不仅能匹配数字,还能匹配字母和下划线,甚至在正则表达式引擎支持的情况下还能匹配汉字!Wow~Cool~,这里的w意思是word,也就是一个单字,记住word或者Wow~你就记住\w了。接下来是\s,s代表space,它可以匹配任意的空白符,包括空格,制表符(Tab),换行符,中文全角空格等,你可以借助死(s)白(b)死(s)白(b)来记住它,或者你想用sb来记住它我也不反对。然后是元字符.,别小看这个不起眼的点,它是最🐂🍺了,前面的三个元字符都只是它的子集而已,它可以匹配除了换行符以外的所有字符。你可以把这个点想象成一个巨大无比的球,它可以在自己的轨道上碾压一切,但就是不可以脱离轨道,就像下面这张图一样:

    暴力的点

    接下来是\b和^以及$,它们都只描述一个位置,并不匹配任何的字符。以\b为例,它匹配的是单词的开始或者结束,首先需要注意的是,这里的单词并不是英文单词,而是指一个及以上连续的\w,所以准确一点说,\b匹配的是:它的前一个字符和后一个字符不全是(一个是一个不是或者都不是\w)。举个例子,如果在一篇文章里搜索hi这个词的话,你会搜出来him、history、high,如果你用\bhi\b搜的话,就会精准地搜出来hi了。这里的b代表boundary,你也可以使用边(b)界来记住它。最后是^和$了,它们分别代表字符串的开始和结束,举个例子,如果你用abc搜索abcdefabcdef,你会搜出来abcdefabcdef两个结果,但是如果你用^abc搜索的话,就只会搜出来abcdefabcdef一个结果,如果你用^abc$搜索的话,匹配就会失败,因为它只能完完整整地匹配abc这个字符串。你可以用这个故事来记住这两个元字符:一个勤劳的草帽编织手工艺人,每天早上(字符串开始)出门卖草帽(元字符^),晚上(字符串结束)卖完草帽挣了很多钱($)回家。

    限定符(重复)

    所谓限定符就是用于把前面的表达式重复N次。比方说我们想匹配11位的手机号码,我们可以这样写\d{11},估计你也能猜到,这里的{11}就代表前面的数字\d重复11次。我们在匹配URL协议时可以这么写^https?,这里的问号?代表前面的s可有可无,所以它可以匹配http和https。我们还有很多种限定符可以用,具体请参考以下表格:

    限定符(重复)

    贪婪与懒惰

    正则表达式默认采取贪婪匹配模式,什么是贪婪匹配模式呢?就是在使得整个表达式得以匹配的前提下,匹配尽可能多的字符。举个例子,如果用a.*b来匹配aabab的话,它会匹配整个aabab字符串,而如果在*后面添加一个问号?使其变成a.*?b再去匹配的话,就会启用懒惰匹配模式,此时就会匹配到aabab和aabab了。上面讲的限定符都可以在后面添加一个?号使其变成懒惰匹配模式,如下图所示:

    懒惰限定符

    字符转义

    有时候我们就是想搜索元字符本身的话该怎么办呢?这个时候我们就得用到反斜线\来取消这些元字符的特殊含义了,例如想搜索www.beibei.com的话就得使用www\.beibei\.com这样的表达式,具体规则如下图所示:

    字符转义

    字符类

    字符类刚刚其实我们已经接触过了,就是用方括号 [] 把目标可选字符括起来就OK了,例如[0123456789]就相当于\d,我们还可以指定一个范围,比方说[0-9]就相当于\d,英文字母就是[a-zA-z],而在不考虑中文的情况下,\w就相当于[0-9a-zA-Z_]。

    需要特别说明的是,很多元字符在方括号内可以不用转义直接使用,例如[.*+?^$()]可以直接匹配到.*+?^$()中的任意一个字符,如下图所示:

    反义

    有的时候,我们想搜索不在某个范围内的字符时该怎么办呢?方法很简单,对于\w、\s、\d和\b的反义只需要把字母大写就可以了,例如:\D匹配任意的非数字,\S匹配任意的非空白符。对于字符类的反义只需要在方括号内用^开头就OK了,这时的^指的就是不在该字符类范围内的意思,而不是字符串的开始,例如[^a-z]匹配的就是非小写英文字母。另外,如果^在方括号内的非开头位置,那么它就代表一个普通的^字符,且不需要转义(还记得上一小节刚刚提到的吗?),我们来看看效果:

    常用的反义代码

    分组

    分组简单来说,就是一个用小括号 () 括起来的子表达式。这么做的用途很多,比方说可以在分组后加上限定符使得整个分组重复N次。例如,我们要匹配一个像192.168.0.100这样的IP地址的话,我们就可以这么写 (\d{1,3}\.){3}\d{1,3}。

    我们来分析一下(\d{1,3}\.){3}\d{1,3}这个表达式,第一部分(\d{1,3}\.)是一个分组,里面是1 ~ 3位的数字后面跟着一个点,然后是{3}把前面的分组重复了3次,最后是一个1 ~ 3位的数字。匹配效果如下图所示:

    分枝条件

    分枝条件就是使用或符号 | 来指定几个表达式,只要其中任意一个表达式匹配成功的话,整个表达式也就匹配成功了。比方说你想要找Chris或者Christopher的话,你就可以使用Chris|Christopher去匹配。

    另外,使用分枝条件时顺序也是非常重要的。举个例子,\d{5}-\d{4}|\d{5}这个表达式用于匹配美国的邮政编码。美国邮编的规则是5位数字,或者用连字号间隔的9位数字。

    如果你把它改成\d{5}|\d{5}-\d{4}的话,那么就只会匹配5位的邮编(以及9位邮编的前5位)。原因是匹配分枝条件时,将会从左到右地测试每个条件,如果满足了某个分枝的话,就不会去再管其它的条件了。

    后向引用

    从这里开始,情况就稍稍变得麻烦一点了。比方说你想要搜索go go或者kitty kitty这样的有重复情况的字符串,那该怎么办呢?如果只用前面讲到的知识点是无法做到的,这时候就需要用到后向引用了。

    正则表达式\b(\w+)\b\s+\1\b即可实现上述需求,我们来分析一下:首先是\b(\w+)\b代表一个左右两边都是边界的单词,然后是\s+匹配不少于1个的空白符,接下来的\1也是我们的重点,它代表前面的\b(\w+)\b中括号内的\w+匹配到的内容,这样就能满足需求了,我们来看看它的效果吧:

    解释一下为什么我们可以这么做,正则表达式引擎在解析正则表达式时,会从左到右扫描每一个分组,以左括号出现的顺序依次为它们分配组号1、组号2……依此类推,各个分组捕获的内容会被保存下来,然后就可以使用\1、\2……来引用前面分组捕获的内容了。补充说一句,分组0会保存整个表达式匹配到的字符串。

    另外,我们还可以通过(?:exp)这样的方式取消该分组对组号的分配权,这么做有什么应用场景呢?当然有!比方说,我要用JavaScript的正则匹配和提取一个简单URL的协议和域名部分,其中协议部分可以省略,我一开始使用的正则是这样的:((https?):)?(\/\/)?([^?]+)

    第一次尝试

    尝试后发现,匹配的结果包含0 ~ 4共5个分组,刨去分组0,其实我只需要分组2(https)和分组4(www.beibei.com),分组1(https:)和分组3(//)都是我不需要的,那么这个时候我就可以取消分组1和分组3对组号的分配权,以便更精准地拿到我想要的结果。改过之后的正则是这样的:(?:(https?):)?(?:\/\/)?([^?]+),注意我们给分组1和分组3的括号内添加了?:的前缀,最终的效果如下:

    第二次尝试

    另外,我们还可以给分组设置自定义的组名,具体方法我就不累述了,请自行参阅《正则表达式30分钟入门教程》的后向引用章节。

    注释

    正则表达式也可以通过 (?#comment) 这样的语法来添加注释,不过我个人并不建议使用它,因为需要添加注释的正则就够复杂了,再在里面加注释就会让正则变得愈加难懂,所以还是把注释加在外面吧。

    零宽断言

    正则表达式的内容还有很多,但是在实战中基本上到这里就够用了,所以零宽断言是我们学习的最后一个也是最复杂的一个知识点。

    首先,如果你看过其他文章对于它们的描述的话,我敢保证你绝对会被那些拗口的、钢铁直男翻译的名词所打败。什么“零宽度正预测先行断言”、“零宽度负回顾后发断言”……WTF??

    可是当你明白了它们的含义,再看看它们的英文单词之后,你可能会有一股想打人的冲动,这么简单的概念为什么被这些人整得这么复杂?这简直比把Socket翻译成套接字更坑爹啊~不扯这些没用的了,我们直接来看它们到底是个啥东东?

    简单来说,它们跟\b、^、$一样,只是描述一个位置,但并不匹配任何的字符。例如,我想要查找James这个单词,但是要求James前面是Lebron加一个空格,也就是说我的目标是Lebron James中的James这个单词,如果我直接搜索James的话,很可能搜出来Harry James、Chris James这样我不想要的东西,那么这个时候我就可以使用(?<=Lebron )James这样的正则来进行搜索,站在(?<=Lebron )的角度,它描述的是我要找到Lebron 这样一个位置,它的后面跟着的是James,所以这个表达式就能帮我准确地找到Lebron James中的James了。

    站在表达式的角度,按照前面或者后面以及是或者不是,我们有以下四种情况:

    Positive Lookahead (?=)  Find expression A where expression B follows: A(?=B)

    Negative Lookahead (?!) — Find expression A where expression B does not follows: A(?!B)

    Positive Lookbehind (?<=) — Find expression A where expression B precedes: (?<=B)A

    Negative Lookbehind (?<!) — Find expression A where expression B does not precedes: (?<!B)A

    看它们的英文单词应该就能明白它们的含义了,不过想要记住它们确实有些困难,所以接下来我就分享一下我的记忆技巧:

    1、首先是一句询问(?)

    2、然后确定位置,你可以把<想象成一个方向箭头,表达式默认放在目标的后面xxx(?),加了方向箭头<就被拉到了目标的前面(?<)target

    3、最后确定是要使用等号=还是使用感叹号!。等号=代表“是”的话那么感叹号“!”就代表不是了。这样四种情况我们就能够很容易地记下来了:)

    再来看一个完整的例子:

    需要特别说明的是,JavaScript早些时候的版本是不支持Positive Lookbehind (?<=)和Negative Lookbehind (?<!)的,也就是带方向箭头<的都不支持,但是我发现Chrome现在也已经支持它们了,但是因为用户的运行环境各异,所以建议还是谨慎使用吧。

    写在最后

    其实我还有“处理选项”和“平衡组/递归匹配”没有提到,“处理选项”建议直接去《正则表达式30分钟入门教程》中查看,“平衡组/递归匹配”我试了一下发现没有哪个在线测试工具是支持的,所以也就不讲了。

    除了我推荐的regex101可以用来在线测试正则表达式之外,还有两个可以以图形化的方式展示正则表达式的网站也很不错,它们分别是:https://regexper.com/https://jex.im/regulex。另外,还有一个不错的交互式学习正则表达式的网站也值得一试:Try Regex

    由于时间仓促以及作者能力有限,文中若有任何错误或者未尽之处,恳请留言批评指正,感谢您的阅读~

    相关文章

      网友评论

          本文标题:轻松入门正则表达式

          本文链接:https://www.haomeiwen.com/subject/ibxnjqtx.html