20分钟脱离搜索引擎构建正则表达式

正则表达式,在现代语言中可以说无处不在。而正则表达式的构成往往伴随着好多符号,我经常会看到别人在搜索引擎和代码界面来回切换以求能构成一个靠谱的正则表达式。我写这篇文章的主要目的是希望能达到看完之后,可以再也不用在构造正则表达式的时候查询搜索引擎了。

正则表达式主要的核心就是符号,因为用搜索字母应该对于一个正常人是没有啥难度的。而这些符号往往是很多人去搜索引擎里主要搜索的内容,但是你静下来仔细想想,计算机里面的常用符号也就那么多。下面的截图是我稍微统计了下gtest里面使用的符号去掉最后的和换行符,大部分符号在任何一个计算机相关的概念里面都会常常遇到,那么就结束这一些废话,进入真正的主题吧。

一、普通字符

虽然上面说了,普通字符搜索哪怕一个非计算机专业人士也会使用。大体步骤就是,首先找到应用程序的搜索功能,然后输入想要的字符串,最后点击搜索。等待一点时间,就能找到或者找不到你所需要的内容。

让我们在这里稍微做多一点点的思考,比如我想搜一个big,计算机的思考过程答题时这样的,先找一个b,然后找一个i,最后找一个g,找到了就返回,找不到就退出。当然,有字符搜索算法是从字符的最后开始匹配。不过不管怎么样,都有天然隐藏一个搜索对比顺序,请记住这一点,然后继续阅读剩下的部分。

二、括号三兄弟

要问在编程里最常见的符号是什么,我一定会选各种括号,大括号,中括号,小括号。在正则表达式中也是这样,不夸张的说,记住或者说掌握吧,掌握这三个括号兄弟,你就掌握了一半以上的正则表达式内容了。按照大小顺序,那么就先从大括号开始吧。

大括号,a{3},在正则表达式里表示multipiers,这个词我觉得最恰当的翻译是倍数器,也就是加倍某个事情。在搜索中,被加倍的只能是搜索的内容。比如说,a{3}就等于aaa,大括号里面是3倍符号。倍数器里面还可以指定倍数的范围,比如a{1,3}表示搜索a,aa或aaa。好学的朋友就要问了,如果是不要下限或者上限会是什么样子的?也就是a{,3}或者a{1,},首先,没有下限是没有意义的,你会搜索不到任何东西,毕竟做啥事都要有下限。而没有上限表示至少一倍,上不封顶,用个专业的计算机术语来说就是贪婪,找到1个,再找1个,直到找不到了为止。再通俗一点,如果你使用a{1,}会把1个a到n个a的结果全部搜索出来。

中括号,[aeiou],正则表达式中使用的最广的一个符号。这个学名是character class,我觉得翻译成符号类有点晦涩,我觉得把这个理解为某种集合中最为恰当,比如说aeiou,表示搜索字符串aeiou,但是[aeiou]表示或者搜索a,或者搜索e,或者搜索i,以此类推。如果非要扯到类上面的话,我觉得就是搜索这一类里面的任意一个就行。那么按照这个思路,如果是[aeeiuooau],其实等同于[aeiou],所以与其说是一个class,不如说是一个set。

那么就到了最后一个了,小括号,((one)|two),和绝大多数的数学概念中代表优先级,在正则表达式里面小括号代表的是分组。那么问题来了,什么是分组?分组在正则表达式里面的最重要的作用就是提供了一个编号机制,这个编号可以在很多地方方便的使用,比如替换。这个到后面才能细说,因为有很多符号还没有介绍。

这就算括号三兄弟的具体作用了,还记得我在第一部分说的一定要记住的天然隐藏的搜索对比顺序吗?现在是第一次让你感受下为什么在正则表达式中要时时提醒自己记住这个,比如有以下的一串搜索字符串:

a[aei]{3}

可以把这个搜索字符串解释为,先搜索a,然后再搜索[aei]{3},而后面这个部分表示先搜索a或者e或者i,最后是这三个或者的字符任意出现三次。有点绕,比如说aaaa符合这个条件,先搜索到了a,然后a是aei中的任何一个字母,并且接下来的a也是,同理最后一个a。以此可以推测,aeai,aaee等等都是符合的,而什么axxx肯定不符合,因为第二个顺序中的条件没有满足,而aei也不行,因为第三个没有满足,eeee也不行,因为第一个没有满足。

所以说正则表达式和普通搜索是一样的,只是换了一种形式,只要记住两点,

按照顺序一个一个的展开,

任何符合只能结合一次,而且搜索是基于单个字符的,

记住这两点,后面会让你知道为什么还有第二点。

至于括号的作用,就是倍数,任一和分组三个作用。

三、三种线

编程语言里有很多种线,什么下划线,中划线。在正则表达式里面主要就是两种线,一个是中划线,一个是back slash,也就是\,还有个就是竖线。

竖线在上面已经介绍过了,和很多编程语言里面的一样,表示或者,one|two,表示搜索字符串one或者搜索字符串two。

中划线很好记忆,中划线在我们生活中也这样使用,表示范围,1-9,很自然的会脑补读出1到9,a-z,自然读出a到z。而在正则表达式中,要配合上面说的中括号使用,因为只有这样,计算机才知道你搜索的不是字符"1-9",而是表示范围里面的任何一个数,你看,集合的概念吧,所以想搜索1-9中任何一个数应该写成[1-9]。

但是这里有个陷阱,

[1-30]

虽然我的脑子里已经读出了1到30,但是在正则表达式里面一定要牢记上一节的两个原则,搜索是按照字符顺序的,而且符号只结合一次。上面的1-30,实际上应该是1-3和0两个部分组成,如果写成[(1-3)0]可能比较清晰,所以这个的意思是,寻找1-3或者0中的任何一个数字,再翻译的强烈一点,应该是0-3中的任一一个数。如果真的想寻找1-30中的任何一个数结合已经介绍的知识可以写出一个可行的方案了。

[0-9]|[1-2][0-9]|30

让我们解析一下这串字符,竖线表示或者,所以上面表示找一个0到9的数,或者第一位是1-2,第二位是0-9的数,也就是10-29,最后是30,这就完成上面说的功能。

最后来说一下back slash,"\",这个符号在正则表达式的意义和所有你能看到的其他地方一样,表示转义,所以转义是转换语义,这样的转换有两种模式,一个是把有意义的符号转化为没有意义的普通符号,一个是把普通字符转换为有特殊意义的符号。比如说,你要是搜索\|a,就是表示搜索|a这个字符串而不是\这个符号或者a这个字符。这个我相信有计算机基础的人都并不陌生,而另一个方向,把普通字符转换为有特殊意义的,比如:

\w 在正则表达式里表示所有字符,相当于[a-zA-Z],w代表word。

\d 表示所有数字,相当于[0-9],d代表digit。

而我觉得正则表达式最有意思的是用同样字母的大写表示非,比如\W,注意是大写,表示非字符,\D表示非数字,我总感觉比如打出GET OUT你都能感受到打字者的负面情绪,所以你只需要记住大写字符代表是反面就行了。

四、加乘和问号

这三个字符在正则表达式里面都表示同一类的意思,都是某种倍数器,虽然扎眼看起来觉得这三个为啥用在一类中,但是说完功能你仔细想想,采用这三个符号还是有一定的直观道理的。

那就先从问号开始,?在正则表达式中表示0次或者1次,相当于{0,1},也就是出现0次或者1次,所以说ab?c和ab{0,1}c是一样的,表示去搜索abc或者ac,b出现一次或者0次。我觉得这个问号很好,有一种,“嗯?有没有?”的感觉,一个问号还是很形象的。

下面是加号,+,表示出现一次或者多次,相当于{1,},ab+c相当于一个字符串,这个字符串是a后面跟着至少一个b再接一个c。

乘号,*,表示出现0次或者多次,相当于{0,},ab*c相当于寻找一个字符串,这个字符串是ac或者a后面跟着至少一个b再接一个c。

关于加号和乘号也很好记忆,你就想着加号对于特殊的自然数0是带有影响力的,0+1是1,而乘号是没有的,0*1为0,所以+表示至少有1个,而乘法表示没有到多个。

五、其他奇怪的符号们

最后是一些我也不知道怎么称呼他们的符号,虽然这是符号类别的最后一个项目,但是不代表这些符号,我起的标题并不代表他们在正则表达式里用的很少,这些符号包括,^,$,.

首先说点号,.,点号表示任一一个字符,相当于很多时候*的意思,但是*已经有其他意思了,比如说a.c,表示找到一个a,然后找到任何一个字符,然后再找到一个c。

下面是^和$,把这两个符号放在一起是因为他们的意义是紧密联系的,^表示一个行的开始,&表示一行的结束,所以^$表示任一空行,使用这个符号你可以完成一行一行的搜索的功能。

但是,有个特殊的情况,如果^出现在[]里面,表示的非的意思,也就是很多语言中的!,比如说[^c]寻找非c的任一字符,而[^abc]表示搜索非a和非b和非c的字符,如果你还记得我前面提过的正则表达式的符号只结合一次的原则的话,貌似这个地方有悖这个原则,但是实际情况不是这样的,因为[]表示任一,你可以理解为里面实际上只有一个字符,所以还是只结合了一次。

六、分组和替换

符号就算介绍完了,最后一个是一个特殊的部分,结合前面的介绍过的小括号的知识,比如我想寻找一个这样的字符串(\d)01,意思是寻找一个三位数,这个三位数以0和1结尾,如果你写\d01,搜索的效果是一样的,

但是有了分组就不一样了,比如说001,那么正则表达式会对结果标记组,上面加小括号的,归为第1组,也就是'0',而一般的程序员会自然有个问题,为啥这里从1开始编号,和计算机的主要思想不一致啊!那么我告诉你,你说对了,实际上真的存在第0组,第0组表示全部结果,也就是001,而如果你使用\d01是没有这个效果的。正则表达式的编组方式是由外而内,什么意思呢,如果你搜索((one)|two),表示搜索one或者two,如果最后目标文件里面含有的是two,那么group 0是two,因为就是你搜索的结果,group 1也是two,因为two包含在外面的一个括号里,而group 2是空,因为根本没有one这个结果,而one是在内的括号。

所以有这种东西,那么在替换的就很方便了,你只要在对应的语言中使用对应的group来作为占位符就可以了,而这个在绝大多数语言中都可以使用。

正则表达式的基本符号就这些了,希望这篇文章能够达到我心中想要达到的目的。这只是一个简单的入门级别的文章,其实你有所了解状态机的话,按照上面的描述,正则表达式其实就是一个状态机的实现,而在这个状态机中,有各种各样的状态变迁。正则表达式就是一个说简单也简单,但是说难也不容易的东西,而且知识点很多,比如还有很多比如说\b表示block开始,这些只要用多了基本上也就记住了,而且只要知道基本的原理,我相信静下新来慢慢的推断还是能找出最后的字符串的。

我试了一下,我看完这篇我自己写的文章大约需要15分钟,那么这篇文章我就叫做20分钟脱离搜索引擎构建正则表达式。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏我杨某人的青春满是悔恨

《编程的智慧(初稿)》读后感

王垠更新了文章,加入了Optional跟Union比较的内容,所以我也来更新一下。垠神认为Optional并没有什么卵用,Java8的Optional我不是很了...

1052
来自专栏take time, save time

你所能用到的数据结构(八)

十一、不能被应用的理论不是好研究 前面介绍了堆栈的一些小小的理论模型,那么这样一个东西有什么作用呢?实际中不可能有那么一辆停在站台前方堵死的火车的,即使有,也...

2704
来自专栏Python数据科学

如何用Python递归地思考问题?

递归是一个很经典的算法,在实际中应用广泛,也是面试中常常会提到的问题。本文就递归算法介绍如何在Python中实现递归的思想,以及递归在Python中使用时的一些...

6376
来自专栏Golang语言社区

转-Golang语言Interface漫谈

一件作品的诞生,通常是一个设计师独立完成的。因为这样,一件建筑也好,画作或者音乐舞蹈也好,才能真实反映出其个性。而正是这种不同于其他同类的独特一面,正是这种发自...

3265
来自专栏企鹅号快讯

PHP弱类型在CTF中的应用

PHP作为世界上最好的语言(然而人生苦短,我用python),在CTF web题中大放异彩,深受出题人的喜爱。P神在对web题出题套路总结的第三条指出,出题人喜...

8095
来自专栏阿杜的世界

《Scala程序设计》阅读书摘

JVM上的语言越来越多了,从前几年的groovy、Scala和Clojure,现在又听说一门Kotlin。对于前三种语言,groovy算是JVM平台上的动态脚本...

902
来自专栏iKcamp

翻译连载 |《你不知道的JS》姊妹篇 |《JavaScript 轻量级函数式编程》- 第 7 章: 闭包 vs 对象

原文地址:Functional-Light-JS 原文作者:Kyle Simpson-《You-Dont-Know-JS》作者 第 7 章: 闭包 vs 对象 ...

2347
来自专栏程序人生

谈谈面向对象编程

最近写了些和函数式编程的文章,有读者和我讨论函数式编程和面向对象编程的优劣。二者都是很好的编程思想,都在着力解决代码重用的问题,也彼此吸收对方的优点,所以大可不...

42411
来自专栏鹅厂优文

Python 工匠:善用变量来改善代码质量

我一直觉得编程某种意义上是一门『手艺』,因为优雅而高效的代码,就如同完美的手工艺品一样让人赏心悦目。

99310
来自专栏码洞

天下无难试之HashMap面试刁难大全

HashMap的结构无疑是Java面试中出现频率最高的一道题,这个题是如此之常见,应该每个人都会信手拈来。可是就在我经历过的无数【允许我夸张一下】面试当中,能完...

1012

扫码关注云+社区

领取腾讯云代金券