You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

50 lines
7.7 KiB
Markdown

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# 导读 | 余晟:我是怎么学习和使用正则的?
你好,我是余晟。受伟忠的邀请,今天我来和你聊聊我是怎么学习和使用正则的。
刚工作那会儿,因为密集用到正则表达式,所以我花了不少时间去钻研正则相关的问题,因此获得了机会,翻译了《精通正则表达式》(第三版),后来又写了一本书《正则指引》。到如今,许多年过去了,这些东西还历历在目,我也很乐意拿出来和你分享一下,希望在学习正则的道路上,能给你一些启发。
我经常在网上看到,许多⼈抱怨正则表达式“难学”,我知道,它确实不好学。但同时,我也去看过大家的抱怨,发现和我之前的做法⼀样:用到什么功能,就去网上搜⼀个例⼦来改改,能跑通就满意。至于这例子到底如何构成的,自己是不是都懂了,其实心里没底,能大概看懂五六分,就已经很满足了。
这样浮光掠影的使用方法或许能解决眼前的问题,但⼀定不算“学会”。它有点像打井,每次挖到⼀点水就满足了,根本不管有没有持续性,也不关心挖没挖到含水层。结果就是,每次要喝水的时候,你都得重新打⼀眼井。
那么对于正则表达式,我们有没有可能打出一口“永不干涸”的深井呢?当然有,那就是**⼀次性多投入点时间,****由表及里,由术及道。一旦掌握了方法,之后就****会****简单很多了****。**
按照我的经验,如果每天花一刻钟或者半小时,坚持个把礼拜,通常都能登堂入室,达到“不会忘”的境界。不要以为这时间很多,我知道有些人很喜欢找“正则表达式五分钟入门”,其实每次都没有入门,日积月累,反而浪费了几十甚至上百个五分钟。
那多投入时间很好理解,但是什么叫掌握方法呢?用我的话说,就是摆脱了字符的限制,深入到概念思维的层面。不要盯着那些鬼画桃符⼀般的字符和表示法皱眉,要摆脱桃符,把真正的“鬼”给认出来——虽然它们不那么容易看见。 也正因为这样,我们才需要⼀次性多投入点时间。
那最终怎样才算“入门”了呢?按照我的经验,就是通过学习掌握方法,后来无论用正则表达式解决什么问题,都能自发遵循下面的流程去走,甚至能达到不需要这个流程,也能做到解决问题,那基本上就算入门了。
**第⼀步,做分解。**拿到一个问题后我们要先思考这个问题可以分为几个子问题每个子问题是否独立我们拿最常见的电子邮件地址匹配来说。从文本结构来看它可以分为“username + @ + domain name”这三个独立的部分。怎么画呢我们可以先画出逻辑结构图。通过这个过程来厘清思路。当然这是软件⼯程最基本的思路相信你做起来应该问题不大。
**第⼆步,分析各个子问题。**某个位置上可能有多个字符?那就用字符组。某个位置上可能有多个字符串?那就用多选结构。出现的次数不确定?那就用量词。对出现的位置有要求?那就用锚点锁定位置…… 某种程度上,这就像武术里的见招拆招,每个问题都有对应的解法,只要熟练掌握了,知道什么时候用字符组,什么时候用多选结构,什么时候用量词,什么时候用锚点,就很容易搭建起完整的概念模型。
**第三步,套皮。** 你大概注意到了,到现在,我们还没有谈论正则表达式的典型标志,比如方括号、星号、花括号。要知道,这些典型标志无非只是一些符号而已,真正重要的是字符组、多选结构、量词等等这些概念。一旦你的概念模型清楚了,写出正则表达式就非常简单了,无非是查阅语法手册,把之前得到的概念模型按照对应语言或工具的约定写下来而已。
许多人觉得正则表达式难懂,总是纠缠于“这里为什么要多一个星号?那里为什么是方括号而不是花括号?”,原因恰恰在于对概念模型不清楚。虽然各种语言或工具对正则表达式的支持大同小异,但细微差别仍然不可忽视。不过只要你心怀正念,洞若观火,这些差异其实并不是大问题。
**第四步,调试。**很多人都说,正则表达式的麻烦之处在于它像个黑箱子,很难调试,迄今为止仍然没有特别好用的⼯具,所以我们没法⼀步步跟进去看匹配的具体过程,只能笼统地知道“匹配了”或者“没匹配”。
那到底怎么调试呢?我的经验是,复杂⼀点的正则表达式不能⼀次写对,这是很正常的。与其纠结“这个正则表达式看起来这么复杂,此处到底要用星号\*还是加号+,不如先搞清楚,星号( \* )或加号( + )限定的到底是正则表达式中的哪一部分,对应要匹配文本中的哪一部分。这两个问题搞清楚了,整个问题就迎刃而解了。
另外,还有⼀点统摄全局的经验想和你说一下, **那就是学会了正则表达式之后,务必要保持克制** 。写正则表达式很容易上瘾,毕竟它的功能那么强⼤,处理速度那么快,⼜像天书符咒那样充满了“神秘”色彩。于是,“写⼀条其他⼈看不懂的正则表达式,⼀次性解决所有问题”,就成了某些程序员的执念。但是,从软件工程的角度来看,这种办法绝对是噩梦,不但其他人无法理解,自己过⼀段时间也会挠头。
那到底该怎么“克制”呢?我的经验有以下三点。
**第⼀,能用普通字符串处理的,坚决⽤普通字符串处理****。**字符串处理的速度不见得差可读性却好上很多。如果要在大段文本中定位所有的today或者tomorrow用最简单的字符串查找直接找两遍明显比to(day|morrow)看起来更清楚。
**第⼆,能写注释的正则表达式,⼀定要写注释**。正则表达式的语法非常古老不够直观为了便于阅读和维护如今大部分语言里都可以通过x打开注释模式。有了注释复杂正则表达式的结构也能一目了然。
**第三,能用多个简单正则表达式解决的,⼀定不要苛求用一个复杂的正则表达式**。这里最明显的例子就是输入条件的验证。比如说常见的密码要求“必须包含数字、小写字母、大写字母、特殊符号中的至少两种且长度在8到16之间”。
你当然可以绞尽脑汁用一个正则表达式来验证但如果放下执念⽤多个正则表达式分别验证“包含数字”“包含小写字母”“包含大写字母”“包含特殊符号”这四个条件要求验证成功结果数大于等于2再配合一个正则表达式验证长度这样做也是可行的。虽然看起来繁琐但可维护性绝对远远强于单个正则表达式。
## 小结
好了,到此为⽌,我的经验介绍完了,可以交棒了。
这些年,很多人问过我,我当时到底是怎么学会正则的?说实话,我那会儿根本没想什么,纯粹出于“干一行爱一行”的朴素想法。要用得多,就找书来,哪怕是囫囵吞枣,也要一鼓作气看完。**我一直觉得,真正值得学的东西,没有什么“平滑学习曲线”。在前面的阶段,你总得狠下心来,过了一个又一个坎儿,然后才能有一马平川。**
我觉得,正则表达式属于“没有维护成本”的技能。一旦学会了,每⼀次遇到这类问题都可以“零成本出击”。所以,长期来看,这绝对是一笔“无本万利”的生意。希望你能通过这个专栏早日达到一马平川!