Python3标准库解读——正则表达式RE

相关模块:Lib/re.py

正则表达式可以处理Unicode字符串,也能处理字节字符串,但是不能混用。正则表达式(Regular Expression,re)是定义pattern的字符串,本质上就是字符串。主要用来对目标字符串进行匹配,找到匹配的字串,对匹配的字串进行处理,如替换、分割等。Python提供了re模块来支持正则表达式。re模块提供了18个函数以及一个Scanner类,但是部分函数和Scanner类并没有放在__all__中。

########################

说明:在这一节,正则表达式用单引号''表示,字符串用双引号""表示。实际使用时,并无此限制,这里只是为了容易区分二者。

再出名几个常用词:

pattern:模式

RE:正则表达式

########################

字符匹配

对于一般的字符构成的pattern,都可以匹配自身。如pattern 'abc'可以匹配字符串"abc"。有一些特殊的字符,不能匹配自身,称为“元字符(metacharacter)”。元字符有“. ^ $ * + ? { } [ ] \ | ( )”。下面首先介绍匹配单个字符的pattern,即[ ]。

[ ]:匹配方括弧[ ]中间的任何一个字符。方括弧中间是一个字符集,可以将所有可以匹配的字符列在里面,也可以指定范围,还可以将这两种混用。如[abc]可以匹配其中的任意一个字符a、b或者c。用-可以指定范围,如[a-z]表示匹配从a到z的任何字符。常用的范围有a-z、A-Z、0-9。混用的例子如[0-9a]表示匹配包括从0到9的数字,以及字母a中的任意一个字符。在字符集中,元字符就没有了其特殊含义,可以跟普通字符一样使用,例如,[abc$]就可以匹配字符a、b、c或者$。如果要匹配不在某个字符集中的字符,就在[ ]中字符集前面加上^,如[^abc]表示匹配不是a、b、c这三个字符的任意一个字符。

最特殊的一个元字符莫过于反斜杠“\”。作为pattern,\后面不同的字符表示不同的模式。用\可以转义元字符,如'\['可以用来匹配方括弧"[";再比如,'\w'可以用来匹配任何数字或者字母,相当于'[a-zA-Z0-9_]'。后面会列出Python标准库中所有的“\”模式。

除了匹配单个字符,正则表达式可以个通过使用通配符来表示模式的重复。常用的通配符有元字符*、+和?。

在pattern中*并不能匹配自身,即'*'不能匹配"*"。*表示在其之前的字符可以出现0次或者多次。如'ca*t'可以匹配"ct"、"cat"、"caat"、"caaat"……

在pattern中,可以用+来匹配在其之前的字符出现一次或更多次,如'ca*t'可一匹配"cat"、"caat"、"caaat"……但是不能匹配"ct",这也是+和*的区别所在。

?表示在其之前的字符出现0次或者1次。

除了上面提到的三个通配符,还有一种表示重复的方式——{m,n}。这表示在其前面的字符出现m到n次,如'ca{1,3}t'可以匹配"cat"、"caat"、"caaat",即字符a至少出现1次,最多出现3次。m、n可以省略,m默认为0。如果省略了n,即表示无上界。通配符*、+和缺少n的{m,}的pattern,匹配其前面的字符的最多次数并非无限制,当前版本上限为两百万。

默认情况下,*、+、?和{m,n}进行贪婪模式的匹配,即匹配尽可能多的内容。例如,用'<.*>'来匹配"<a>b<c>"时,会匹配整个字符串。在通配符后面添加一个?,则进行非贪婪模式的匹配,即匹配尽可能少的内容。在上面的例子中,如果采用的RE是'<.*?>',则会匹配到"<a>"。

下面列出了正则表达式中常见的'\'特殊用法:

  • \number:匹配数字nunber对应的group的内容(group在后面介绍)。例如,'(.+) \1'可以匹配"55 55"。但是,如果\后面的number为0或者3位八进制的数,则不是进行group匹配,而是表示该八进制数所代表的字符。如果该模式出现在[ ]中间,则number中的每个数字就是代表单个的数字字符。
  • \A:匹配字符串的开始。
  • \b:匹配词开头或者结尾处的空字符串。“词”,即由数字、字母、下划线所组成的字符序列。如r'\bfoo\b'可以匹配"foo"、"foo."、"(foo)"。
  • \B:与\b相反。匹配非词开头或结尾处的空字符串。
  • \d:对于Unicode模式,匹配任何数字字符。对于bytes模式,匹配任何数字,与[0-9]相同。
  • \D:匹配任何非数字字符。
  • \s:对于Unicode模式,匹配Unicode空白字符,包括空格、制表符、换行符等。对于bytes模式,匹配ASCII字符集中的空白字符,等同于[ \t\n\r\f\v]。
  • \S:与\s相反,匹配任何非空白字符。
  • \w:对于Unicode模式,匹配任何字母字符.对于bytes模式,相当于[a-zA-Z0-9_]
  • \W:与\w相反,匹配任何非字母字符。
  • \Z:匹配字符串的结尾。

正则表达式的使用

前面介绍了简单的匹配字符的正则表达式,接下来介绍正则表达式的使用。Python的re模块给使用正则表达式提供了一个接口。正则表达式的使用一般分为三步:编译得到正则表达式对象、通过正则表达式对象匹配目标字符串得到匹配对象、通过匹配对象获取匹配信息以及相关操作。

一、正则表达式对象

使用正则表达式之前,需要将自定义的pattern编译为正则表达式对象(或则称谓“模式对象”),这个对象也代表了pattern对应的正则表达式。通过正则表达式对象提供的成员函数,可以进行各种操作,如进行查找匹配模式的字串、替换等。re模块编译正则表达式对象的函数为re.compile,例如,po=re.compile('ab*'),po就是经过编译得到的正则表达式对象。

传给re.compile函数的代表pattern的参数,也是字符串,即是正则表达式——RE。在RE中经常使用\来转义特殊字符。如果要匹配的目标字符串中包含有特殊字符,如\,那么编写RE时,就要注意\的使用。例如,要匹配的字符串为某个文件中的一个字符串,原文为"\code",而实际上,在正常的字符串中,'\'的表示是用双反斜杠来转义的,即为'\\'。也就是说,实际要匹配的字符串为"\\code"。在写RE时,需要匹配一个\,则在RE中需用'\\',匹配"\\code"的RE即为'\\\\code'。也就是说,要匹配一个"\",RE中则需要对应的pattern为'\\\\'。

Python提供了一种简化的写法,即原始字符串(Raw String)。在字符串前面加上字母r,即禁止字符串中进行转义,例如,r"\n"就是一个长度为2的字符串,包含了两个字符'\'和'n'。RE经常采用这种原始字符串来进行简化。

前面讲到用re.compile可以得到一个代表正则表达式的正则表达式对象po。compile函数的形式为re.comile(pattern,flags=0)。函数的pattern即为正则表达式。flags指定了正则表达式的工作模式,默认为0。flag为在下面的值中取值,并可将所取多个值进行OR(|)运算。

  • re.A,re.ASCII:选择此标志,则\w、\W、\b、\B、\d、\D、\s以及\S只进行ASCII匹配,仅适用于Unicode模式,对于bytes模式不适用。
  • re.DEBUG:显示编译的表达式的debug信息。
  • re.I,re.IGNORECASE:匹配时,不区分大小写。
  • re.L,re.LOCALE:在此模式下,\w、\W、\b、\B、\s以及\S取决于当前的locale。因为locale机制非常不可靠,不建议适用该组标志。从Python3.6开始,re.LOCALE只能用于bytes模式,且与re.ASCII不兼容。
  • re.M,re.MULTILINE:指定该标志,则'^'匹配整个字符的开始以及每一行字符串的开始。'$'匹配整个字符串的结尾以及每一行字符串的结尾。未指定该标志,即默认情况下,'^'仅匹配整个字符串的开始,'$'仅匹配整个字符串的结尾。
  • re.S,re.DOTALL:在该模式下,'.'匹配包括换行符在内的任何一个字符;未指定该标志,则'.'匹配除换行符之外的任意一个字符。
  • re.x,re.VERBOSE:指定该标志,则可以在写传递给comiple的pattern参数时进行换行、注释,使得RE更具可读性。

二、匹配操作

得到po对象之后,就可以对目标字符串进行匹配操作。po对象包含了几个成员函数:po.search()、po.match()、po.fullmatch()、po.split()、po.findall()、po.finditer()、po.sub()、po.subn()。下面分别进行介绍。

po.search(string[,pos[,endpos]]):string为要进行匹配得目标字符串。在string中查找po匹配的字符串第一次出现的位置,返回对应的匹配对象(match object,mo)。如果string中并没有与po匹配的字符串,返回None。对于可选参数pos,默认值为0,表示在string中开始查找的起始位置。endpos表示匹配的终点。该函数功能为:在string的pos到endpos-1中查找与po匹配的字符串,返回匹配对象。

  • po.match(string[,pos[,endpos]]):与po.search功能相同,不同的是match只在pos指定的位置pos作为RE的开头进行匹配,search会从pos开始依次往后匹配只到找到一个匹配的子串或者返回None。
  • po.fullmatch(string[,pos[,endpos]]):如果整个string[pos,endpos]字符串匹配po,返回对应的mo,否则返回None。
  • po.split(string,maxsplit=0):将string用与po所匹配的字串进行分割。
  • po.findall(string[,pos[,endpos]]):在string[pos,endpos]字符串中从左至右查找与po匹配的所有非重叠子串,并将这些子串组成为一个list并返回该list。
  • po.finditer(string[,pos[,endpos]]):功能与findall相同,返回的不是list,而是一个iterator。
  • po.sub(repl,string,count=0):将string中与po匹配的字符串用repl替换,并将替换得到的字符串返回。count指定替换的最大次数,为0时表示替换全部。
  • po.subn(repl,string,count=0):与po.sub类似,但是返回的是一个tuple, (new_string, number_of_subs_made)。

re模块提供了与po成员函数对应的函数,这些函数实际上还是先调用compile函数,通过返回的正则表达式对象再调用其成员函数。如re.match(pattern,string)其实就是两步:po=re.compile(pattern),po.match(string)。在用一个RE对大量的字符串进行匹配时,应该先调用compile函数生成一个po,利用po进行匹配,这样比直接调用re.match之类的函数进行匹配性能上要提高很多。

re模块提供的相关函数有:re.search()、re.match()、re.fullmatch()、re.split()、re.findall()、re.finditer()、re.sub()、re.subn()。此外,re模块还有两个函数re.escape(string),re.purge()。

re.escape(string):转义string中处了字母、数字、_之外的所有字符。

re.purge():清除正则表达式缓存。

在进行匹配操作的同时,可以通过使用圆括弧'( )'将一个RE分为几组来分别匹配字符串中的几个部分并将其分别返回。例如,电子邮件的头部有"From:author@example.com"、"To:user@example.com",可以用'(\w*):(\w*)'将冒号两边的字符串分开并通过mo.group函数单独返回:po=re.compile(r'(\w*):([\w\W]*)');mo=po.search('From:author@example.com');mo.groups()。

三、匹配对象(match object,mo)

在匹配操作中,如果匹配到与po对应的字符串,则会返回匹配对象mo。通过mo可以获取与匹配操作以及RE相关的信息。

  • mo.expand(template):对字符串template中的\转义用mo中对应的group进行替换,返回结果字符串。如'\1'就用mo.group(1)替换。
  • mo.group([g1,...]):返回匹配对象mo的指定子组,即RE中用圆括弧包含的部分。mo.group(1)返回匹配到的RE中第一个圆括弧部分的内容。如果指定多个子组,则返回一个由这些子组组成的一个tuple。对于RE中'(?P<name>...)'部分,则用name返回,即mo.group(name)这种形式返回与'...'匹配的内容。
  • mo.groups():返回由所有子组组成的一个tuple。
  • mo.groupdict():返回一个包含所有'(?P<name>...)'形式的子组组成的一个dict,其key为name。
  • mo.start([gNumber]),mo.end([gNumber]):返回gNumber指定子组的开始、结束位置。gNumber默认为0,即对应匹配到的整个子串。
  • mo.span([gNumber]):gNumber默认值为0.返回一个tuple,(mo.start(gNumber),mo,end(gNumber))。

关于group

这里把group单独列出来进行介绍,是因为在使用正则表达式提取信息时常用到group。所谓的group,就是在写RE时,将感兴趣的内容包含在圆括弧内。例如,前面介绍的Email的例子,通过正则表达式r'(\w*):([\w\W]*)'匹配"From:author@example.com",得到的匹配对象mo,mo.group(1)就是'From',group(2)是'author@example.com',group(0)为'From:author@example.com'。

group除了按照默认顺序提取,还可以给子组取名字,即在RE中使用(?P<name>...)的形式。获取到mo后,即可通过mo.group(name)获取对应信息。如果将上例中的RE改为r'(\w*):(?P<email>[\w\W]*)',得到的mo既可以通过上面介绍的按照group顺序取出信息,也可以用mo.group('email')获取邮件字符串group(2),即'author@example.com'。

以上是group常见的两种用法,通过这个例子,可以对group有个大概的了解。要想深入学习其他group形式,可以参考文档进一步学习,在以后也会单独列出一个文章进行介绍。

--------------------------------------------

扫一扫,关注我们的微信,及时获取信息!

转载于:https://my.oschina.net/aomojan/blog/818173

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值