正则表达式高阶技巧之详解转义(使用python实现)

元字符的转义

  • 在已经了解了字符串与正则表达式的关系后,再来学习正则表达式中的元字符的转义。元字符是在正则表达式具有特殊含义的字符,如果要匹配“元字符”自身则必须要进行转义,也就是在元字符之前添加\。如下表展示各种结构的转义:
结构记法转义说明
字符组[ ]\[ ]只对开括号进行转义即可
.\.
-\-[a\-b]等价于[-ab],匹配a、b、- 三个字符
量词*    +   ?\*   \+   \?
*?    +?   ??  \*\?    \+\?   \?\?  
{m,n}\{m,n}只对开花括号转义
括号(....)\(....\)开、闭括号都需要进行转义
多选结构|\|竖线(或)需要转义
括号和多选结构(..|..)\(..\|..\)开、闭括号和都需要进行转义
断言^    $\^   \$
替换引用$num\$或$$在替换的replacement字符串转义
  • 从上表可以看出,对称出现的元字符在转义时并不是“对称”的,比如与开方括号[对应的闭方括号],与开花括号{对应的闭花括号},这两个字符是否是元字符,取决于之前是否出现了开方括号或开花括号,如果有,则作为元字符出现;否则为,仅仅作为普通字符出现。如下表解释此问题:
正则表达式解释可匹配字符串
[ab]字符组[ab]a或者b
\[ab]字符[、字符a、字符b、字符][ab]
ab]字符a、字符b、字符]ab]
  • 在在字符组内部的闭括号]在任何情况都是需要转义的,否则类似于[]]的正则表达式会出现二义性,造成解读错误,所有能匹配字符a、字符b、字符]的字符组应该写成[ab\]],而不是[ab]]。如上,括号内部的任何闭括号)都要转义,比如包含abc)的多选结构的正则表达式就应该写成(ab|c\)),而不是(ab|c))

彻底消除元字符的特殊含义

  • 在某些情况是需要消除所有元字符的特殊含义,使其全部作为普通字符。这种情况经常发生在需要处理用户输入的场合:用户输入某个字符串,需要根据这个字符串进行正则查找
  • 假设某个文件中包含多个文本片断,用空格隔开,现在需要查找包含用户输入内容的片段:用户输入cat,查找包含cat的行,直接思路为^.*cat.*$(使用多行模式,同时总不能指定为单行模式),假如用户输入的内容保存在变量userInput中,就应该使用"^.*"+userInput+".*$"得到需要查找用的正则表达式
  • 如上,用户输入cat,这样做肯定时没问题。如果用户输入ca*t,得到的正则表达式为^.*ca*t.*$,本意是查找包含字符串catt的行,但是在正则表达式为元字符,正则表达式ca*t可以匹配cat、caaat、cabct,但就是不能匹配ca*t,就是发生错误
  • 更为麻烦的是:如果碰到恶意用户可能会输入a(b*b*)b*之类的恶意字符串,这样的正则表达式匹配起来会消耗服务器大量资源,这也就是所说的表达式拒绝服务攻击,严重时可能会导致服务器瘫痪
  • 为了解决上述的问题,一些语言中的正则表达式提供了特殊的结构,彻底来消除元字符的特殊含义,提供真正的“安全”的表达式(也就是普通的字符串),在python中可以使用escape方法,如下举例:
import re

re.search(re.escape("ca*t"),'cat') is not None
re.search("ca*t",'cat') is not None

在这里插入图片描述

  • 我们可以查看escape方法的返回值如下:
    在这里插入图片描述
  • 我们在以后获取用户输入的值时,需要仔细辨别并消除用户输入字符串中元字符的特殊含义,是不可忽略的步骤

字符组的转义

在正则表达式中,如果需要使用表示作为元字符的普通字符串(如*、?等等),就是需要转义的,特殊的是,常见的元字符出现在字符组内部基本上都不算元字符,也就是说,他们在字符组内部出现时,不需要转义,如下测试:

import re

re.search(r"[*]",'*') is not None
re.search(r"[?]",'?') is not None
re.search(r"[(]",'(') is not None

在这里插入图片描述
在之前也有提到过,字符组有自己的元字符规定,也有相应的转义规定:在字符组内部,只有三个字符需要转义

  • 一个是闭方括号],如果不是作为字符组结束标志的闭方括号,则必须写成\],如[0\]9],它可以匹配的字符是0、]、9
  • 一个是横线-,如果不是用于范围表示法(比如[0-9]),则必须写成\-,如[0\-9],它可以匹配的字符是0、-、9,如果它紧跟在开方括号之后,也可以不需要使用转义,[0\-9][-09]是等价的,在这里更推荐使用后一种表示形式,更加简洁清晰
  • 还有一个需要转义的字符是^,如果它不是用于排除型字符组([^ab]),则必须写成\^,如[\^ab],它可以匹配除^、a、b之外的任何字符,如果他不是紧跟在方括号之后,也不用转义。[\^ab]、[a^b]、[ab^]这三个是完全等价的,在这里更推荐使用后两种表示形式,更加简洁清晰

如下举例:

import re

# 未转义的]
re.search(r"[0]9]", ']') is not None

# 转义的]
re.search(r"[0\]9]", ']') is not None

在这里插入图片描述

# 未转义的-
re.search(r"[0-9]", '-') is not None

# 转义的-
re.search(r"[0\-9]", '-') is not None

re.search(r"[-09]", '-') is not None

在这里插入图片描述

# 未转义的^
re.search(r"[^ab]",'^') is not None

# 转义的^
re.search(r"[\^ab]",'^') is not None
re.search(r"[a^b]",'^') is not None
re.search(r"[ab^]",'^') is not None

在这里插入图片描述

正则表达式的处理形式

在之前的学习中我们主要使用的是python语言,其具体的办法是调用re这个包(package)中的方法(函数),比如re.search()、re.findall()、re.sub()。选择合适的函数,将正则表达式和字符串传入,这是一种常见的方法

函数式处理

  • 在函数式处理中,正则表达式的常见操作(查找、替换等)都有对应的函数,执行这些操作时,调用对应的函数,将正则表达式和字符串作为参数传入即可,Python、PHP是函数式处理的典型代表,如下:
import re

# 查找
re.findall(r"\d+", '12 45 66 wy')

# 替换
re.sub(r"\d+", r'[\g<0>]', '12 45 66 wy')

在这里插入图片描述

面向对象式处理

  • Java与.NET之类的语言的正则表达式采取不同的处理方式:在进行正则表达式处理之前,必须生成专门的正则表达式对象(在不同的语音里,对象所属的类名不同),再调用此对象的成员函数

比较

  • 同样是进行查找操作,如果我们使用函数式处理,只需要调用对应的函数:但使用面向对象式处理,需要逐步生成各种对象,再调用对象自身的方法。看起来,使用后者是要麻烦很多的,采用这种处理方式有什么好处呢?
  • 之前在正则转义的介绍中讲过,正则表达式并不等于字符串,即便是正则表达式是以字符串的形式给出的,再进行正则表达式操作之前,必须首先生成专用的“正则表达式对象”,函数式处理隐去了生成过程,感觉更加直接;面向对象式处理则暴露了生成的过程,感觉更加细致
  • 只要执行正则表达式操作,就会产生正则表达式对象;所以,面向对象式处理的代码虽然较为繁琐,但是如果在后续的的处理中如果需要重复使用某些正则表达式,它的效率往往是比函数式处理更高的,在面向对象式的处理中,可以将已经生成的对象作为变量保存起来,就不必重复生成了
  • 面向对象式处理的步骤分明、多次使用的效率更高,而函数式处理的好处是方便顺手、代码简洁;最好的办法是根据应用的场合不同,采取不同的处理方式;如果正则表达式只是单次使用,则使用函数式处理即可;如果正则表达式需要重复使用,则选择面向对象式处理。实际上,许多的编程语言也提供了两种不同的设计,比如在Python中也提供了一些面向对象式处理的方法,Java与.NET也提供了一些函数式处理
  • 在Python中进行面向对象式处理,可以先调用re.compile()生成专门的RegexObject对象(也就是我们所说的正则表达式对象),在进行正则表达式操作时,可以把这个对象作为参数,传递给相应的函数,另一方面,re中的各个函数也可作为对象自身的成员方法,我们也可以使用已经生成好的RegexObject对象调用这些方法,如下测试:
import re

# 函数式
re.findall(r"\d+", '12 66 wy')

# 面向对象式
# 1.生成re对象
digitRegex = re.compile(r"\d+")
# 1.1调用re.findall方法
re.findall(digitRegex, '12 66 wy')
# 1.2直接调用re对象的方法
digitRegex.findall('12 66 wy')

在这里插入图片描述

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值