一时半刻谈正则(下篇)
原文链接:https://www.f2er.com/regex/362348.html
6.
神奇的转义
有个重要的问题我尚未提及,即:如果需要匹配的某个字符本身就是元字符,又该如何处理呢?例如你想要检索互联网主机名www.baidu.com使用w{3}.\w*.(com|cn|org|net) 这样的式子可以么?由于点号. 本身就是元字符,它可以匹配任意字符包括空格,所以这个式子是可以匹配成功的。
真正匹配文本中点号的元序列应该是反斜线加上点号的组合 \. 称之为转义的点号。这样的办法适用于所有的元字符。不过在字符组内部无效。很多编程语言都采用这种方式来转义其自身特殊的字符。反斜线的作用会使得它作用的元字符失去特殊含义,成为普通字符。我们可以使用\([a-zA-Z]+\)来识别一个括号内的单词,比如(hello)。如果反斜线后面不是跟的元字符,那就要看具体情况而定了。
-----------------------------------------------------------------------------------------------------------------------
7. 单词分界符
不知你遇到过这个问题没有,期望匹配的某个单词包含在另一个单词之中。例如 Believe in yourself. 你想匹配you这个单词,于是你采用了you 作为表达式,但是你会发现它匹配成功了。这显然不是我们所希望的。因为解决这个问题的方法并不是每种版本的正则都支持的,所以我放到了这里来说明。
如果你的正则表达式支持元字符序列 \< 和\> 的话,那么这个反斜线和大小于号的组合就可以用来匹配单词的开始和结束位置。你已经知道了脱字符 ^和美元符号$ 用于匹配行首和行尾的位置,那么这两个字符序列,用于匹配单词开始和单词结束的位置。请注意:< 和 > 本身并不是元字符,只有和\ 结合起来的时候整个序列才具有特殊意义。
除了\< 和 \> 以外我还将介绍一个字符序列\b ,不是 \d 哦,它同样用于匹配一个位置,单词的开始和结束。 例如\<you\> 和\byou\b 具有相同的效果。
----------------------------------------------------------------------------------------------------------------------
8.反义字符
有时需要查找不属于某个能简单定义的字符类的字符。比如想查找除了数字以外,其它任意字符都行的情况,这时需要用到反义字符,你已经见到过了排除型字符组,下面是几个便捷的形式:
发现了吧,就是相对应的符号的大写形式。这是不是很好记忆呢?关于这个就不再详细说明了,因为下面还有更为重要的任务。
---------------------------------------------------------------------------------------------------------------------
9.非捕获型括号
我们在本节内容前面讲括号的时候讲到了反向引用。正则表达式引擎在处理分组的时候为了记住它所捕获到的内容是需要耗费内存的,然而通常情况下,我们并不需 要记住括号里面的内容。只是想简单的分一下组。基于此原因,出现了非捕获型括号这样一种特殊字符序列。它是这样表示的(?: )亦即一个开括号后面紧跟一个?然后跟一个冒号:表示只分组而不捕获。例如(?:\d{3})(\d{3})(\d{4}) 来匹配 0123456789的话,使用\1引用得到的内容为345 而\2得到的是6789。
使用非捕获型括号的好处有两点。第一是为了不必要的分组捕获,就是保存分组中匹配的内容。提高效率。第二就是总的来说,根据情况选择合适的括号,能够让程序更为清晰,看代码的人不会被括号的具体细节所困扰。
-------------------------------------------------------------------------------------------------------------------
10.顺序环视与逆序环视
在之前,我们已经接触到了几种表示匹配一个位置的元字符或元字符序列。分别是 ^ 匹配行首, $匹配行尾, \<和 \b匹配单词开始位置,\> 或\b 匹配单词结束位置。这是否已经够用了呢?在实际应用过程中,人们发现,仅仅有这些匹配位置的解决方案是远远不够的。于是出现了几种新的字符序列:
好吧,不明觉厉。看起来挺复杂的样子。接下来我会重点讲解。首先记住一个概念:环视结构不匹配任何字符,只匹配文本中的一个位置,这一点与单词分解符,以及^和$是一样的。
11.标准量词匹配优先原则(贪婪模式与非贪婪)
我不晓得是否应该在这里讲这部分内容。因为它相对来说不容易理解。这部分内容或许当你理解了正则表达式内部工作原理之后会比较容易明白,所以在这里就不说 了。因为我的目的是让你会用。虽然距离真的学会正则表达式还有很长一段路要走,不过现在已经是个很不错的开始了。
------------------------------------------------------------------------------------------------------------------------
没有讲到的部分
有个重要的问题我尚未提及,即:如果需要匹配的某个字符本身就是元字符,又该如何处理呢?例如你想要检索互联网主机名www.baidu.com使用w{3}.\w*.(com|cn|org|net) 这样的式子可以么?由于点号. 本身就是元字符,它可以匹配任意字符包括空格,所以这个式子是可以匹配成功的。
真正匹配文本中点号的元序列应该是反斜线加上点号的组合 \. 称之为转义的点号。这样的办法适用于所有的元字符。不过在字符组内部无效。很多编程语言都采用这种方式来转义其自身特殊的字符。反斜线的作用会使得它作用的元字符失去特殊含义,成为普通字符。我们可以使用\([a-zA-Z]+\)来识别一个括号内的单词,比如(hello)。如果反斜线后面不是跟的元字符,那就要看具体情况而定了。
-----------------------------------------------------------------------------------------------------------------------
7. 单词分界符
不知你遇到过这个问题没有,期望匹配的某个单词包含在另一个单词之中。例如 Believe in yourself. 你想匹配you这个单词,于是你采用了you 作为表达式,但是你会发现它匹配成功了。这显然不是我们所希望的。因为解决这个问题的方法并不是每种版本的正则都支持的,所以我放到了这里来说明。
如果你的正则表达式支持元字符序列 \< 和\> 的话,那么这个反斜线和大小于号的组合就可以用来匹配单词的开始和结束位置。你已经知道了脱字符 ^和美元符号$ 用于匹配行首和行尾的位置,那么这两个字符序列,用于匹配单词开始和单词结束的位置。请注意:< 和 > 本身并不是元字符,只有和\ 结合起来的时候整个序列才具有特殊意义。
除了\< 和 \> 以外我还将介绍一个字符序列\b ,不是 \d 哦,它同样用于匹配一个位置,单词的开始和结束。 例如\<you\> 和\byou\b 具有相同的效果。
----------------------------------------------------------------------------------------------------------------------
8.反义字符
有时需要查找不属于某个能简单定义的字符类的字符。比如想查找除了数字以外,其它任意字符都行的情况,这时需要用到反义字符,你已经见到过了排除型字符组,下面是几个便捷的形式:
发现了吧,就是相对应的符号的大写形式。这是不是很好记忆呢?关于这个就不再详细说明了,因为下面还有更为重要的任务。
---------------------------------------------------------------------------------------------------------------------
9.非捕获型括号
我们在本节内容前面讲括号的时候讲到了反向引用。正则表达式引擎在处理分组的时候为了记住它所捕获到的内容是需要耗费内存的,然而通常情况下,我们并不需 要记住括号里面的内容。只是想简单的分一下组。基于此原因,出现了非捕获型括号这样一种特殊字符序列。它是这样表示的(?: )亦即一个开括号后面紧跟一个?然后跟一个冒号:表示只分组而不捕获。例如(?:\d{3})(\d{3})(\d{4}) 来匹配 0123456789的话,使用\1引用得到的内容为345 而\2得到的是6789。
使用非捕获型括号的好处有两点。第一是为了不必要的分组捕获,就是保存分组中匹配的内容。提高效率。第二就是总的来说,根据情况选择合适的括号,能够让程序更为清晰,看代码的人不会被括号的具体细节所困扰。
-------------------------------------------------------------------------------------------------------------------
10.顺序环视与逆序环视
在之前,我们已经接触到了几种表示匹配一个位置的元字符或元字符序列。分别是 ^ 匹配行首, $匹配行尾, \<和 \b匹配单词开始位置,\> 或\b 匹配单词结束位置。这是否已经够用了呢?在实际应用过程中,人们发现,仅仅有这些匹配位置的解决方案是远远不够的。于是出现了几种新的字符序列:
好吧,不明觉厉。看起来挺复杂的样子。接下来我会重点讲解。首先记住一个概念:环视结构不匹配任何字符,只匹配文本中的一个位置,这一点与单词分解符,以及^和$是一样的。
- 肯定顺序环视(?=expression)在遇到此类表达式时,正则表达式内部匹配顺序从左至右查看文本,尝试匹配expression,如果某个位置的右侧能够匹配expression,则返回匹配成功的信息。举个例子(?=\d) 表示如果当前位置的右边是一个数字,则匹配成功。 匹配这句话: My age is 20. 时,(?=\d)匹配的位置有两个My age is20. 请注意它匹配的并不是空格,而是为了展示匹配的位置,所以用红色竖线标志出来了。再举个例子:\w+(?=ing)或者 \w+(?=ing) 它匹配以ing结尾的单词前半部分。 例如: I was reading while she was writing. 这个表达式匹配成功的部分为 I wasreading while she waswriting. 看下图,这是用我的测试工具测试的结果,绿色高亮部分表示匹配成功部分。我想你应该能够理解肯定顺序环视的意思了吧?
- 否定顺序环视(?!expression), 否定顺序环视的意思就是匹配一个位置,这个位置的右侧不能匹配expression。还是用例子来说明,我们这次用\w(?!ing)它表示匹配一个以ing结尾的单词中在ing前面的一个字母。在I was reading while she was writing.中由于reading单词中d字符的右侧是ing所以d不能匹配成功。同理writing中的t。
- 如果你已经理解了顺序环视中的两部分,那么逆序环视应该可以很容易明白了。肯定逆序环视(?<=expression)表示匹配一个位置,此位置的左侧能够匹配expression。还是用(?<=\d)来匹配My age is 20. 结果是My age is 2 0并不是说匹配了点号.而是匹配这两个位置,因为这两个位置左侧能够匹配\d。看运行结果。绿色竖线表示匹配的位置。
- 否定逆序环视(?!=expression)表示匹配一个位置,此位置的左侧不能够匹配expression。不再详述。
11.标准量词匹配优先原则(贪婪模式与非贪婪)
我不晓得是否应该在这里讲这部分内容。因为它相对来说不容易理解。这部分内容或许当你理解了正则表达式内部工作原理之后会比较容易明白,所以在这里就不说 了。因为我的目的是让你会用。虽然距离真的学会正则表达式还有很长一段路要走,不过现在已经是个很不错的开始了。
------------------------------------------------------------------------------------------------------------------------
没有讲到的部分
- 标准量词匹配优先原则(很重要)
- 注释
- 处理选项(包括单行模式,多行模式,忽略空白和注释等,不同平台下支持的情况有所不同,所以我没有在这里说明。)
- 额外的许多不太常用的语法,例如固化分组(?>),字符编码支持,递归匹配等。
- 正则引擎(正则表达式匹配原理)(非常重要)
- 如何提升正则表达式的效率
---------------------------------------------------------------------------------------------------
不知道你从头到尾看到这里经过了多少时间,如果是初学者,我想应该不止1小时。请原谅我在开始对你说一小时了。如果你真的在一小时内读完了,我在佩服之余还希望你能安下心来,自己在找一些教程好好钻研钻研。正则表达式不仅仅是一门技术,更是一门艺术。
这篇教程似的博客是我在学习完正则表达式之后写的。花了零零散散两天时间。其中内容难免有很多纰漏,还请读者不吝指正。当然教程中还有很多细节没有提到。因为我的目的并不是要写一篇大全。所以有兴趣探讨相关知识的朋友们可以邮件给我。
Email: proliming@gmail.com
推荐网站:
推荐书籍: 《精通正则表达式》 O'Reilly 出版社,Jeffrey E.F. Friendl 著