本节内容


  1. re模块介绍

  2. 使用re模块的步骤

  3. re模块简单应用示例

  4. 关于匹配对象的说明

  5. 说说正则表达式字符串前的r前缀

  6. re模块综合应用实例

  7. 参考文档

提示: 由于该站对MARKDOWN的表格支持的不是很好,所以本文中的表格均以图片的形式提供,大家如果看着比较模糊,可以放大来看或下载图片在本地查看。

正则表达式(Regluar Expressions)又称规则表达式,在代码中常简写为REs,regexes或regexp(regex patterns)。它本质上是一个小巧的、高度专用的编程语言。 通过正则表达式可以对指定的文本实现
匹配测试、字串/内容查找、子串/内容替换、字符串分割 等功能。正则表达式的语法和使用不是本节要讲的内容(关于正则表达式的详细介绍请参考另一篇博文《正则表达式总结》),本节主要介绍的是Python中是如何使用re模块来完成正则表达式的相关操作的。

一、re模块介绍


Python中的re模块提供了一个正则表达式引擎接口,它允许我们将正则表达式编译成模式对象,然后通过这些模式对象执行模式匹配搜索和字符串分割、子串替换等操作。re模块为这些操作分别提供了模块级别的函数以及相关类的封装。

1. re模块提供的类

Python中的re模块中最重要的两个类:

描述
Regular Expression Objects正则表达式对象,用于执行正则表达式相关操作的实体
Match Objects正则表达式匹配对象,用于存放正则表达式匹配的结果并提供给用于获取相关匹配结果的方法

正则表达式对象类中定义的方法属性

通过re模块的compile()函数编译得到的正则表达式对象(下面用regex表示)支持如下方法:

电脑培训,计算机培训,平面设计培训,网页设计培训,美工培训,Web培训,Web前端开发培训

参数说明:
  • string: 要匹配或处理的字符串

  • pos: 可选参数,表示从string字符串的哪个位置开始,相当于先对字符串做切片处理string[pos:]

  • endpos: 可选参数,表示到string字符串的哪个位置结束(不包含该位置)

  • maxsplit: regex.split()方法的可选参数,表示最大切割次数;默认值为0,表示能切割多少次就尽可能多的切割多少次

  • count: regex.sub()和regex.subn()方法的可选参数,表示最大替换次数;默认为0,表示能替换多少次就尽可能多的替换多少次

  • repl: sub和subn函数中的repl表示replacement,用于指定将匹配到的子串替换成什么内容,需要说明的是该参数的值可以是一个字符串,也可以是一个函数

说明: 如果指定了pos和endpos参数,就相当于在进行正则处理之前先对字符串做切片操作 string[pos, endpos],如rx.search(string, 5, 50)就等价于rx.search(string[5:50]),也等价于rx.search(string[:50], 5);如果endpos比pos的值小,则不会找到任何匹配。

匹配对象类中定义的方法和属性

调用正则表达式对象的regex.match()、regex.fullmatch()和regex.search()得到的结果就是一个匹配对象,匹配对象支持以下方法和属性:

电脑培训,计算机培训,平面设计培训,网页设计培训,美工培训,Web培训,Web前端开发培训

参数说明:
  • template: m.expand()方法中的template参数是一个末班字符串,这个字符串中可以使用分组的数值后向引用(如:\1,\2)或命名后向引用(如\g<1>,\g<NAME>)来表示某个分组的占位符;m.expand()方法的执行过程实际上就是通过sub()方法把template字符串中的这些分组占位符用当前匹配对象中的数据进行替换。

  • default: m.groups()与m.groupdict()方法中的default都是为为匹配成功的捕获组提供默认匹配值的。

  • group: m.group()、m.start()、m.end()和m.span()方法中的group参数都表示要选择的分组索引值,1表示第一个分组,2表示第二个分组,依次类推,group参数的默认值是0,表示整个正则表达式所匹配的内容。

2. re模块提供的函数

re模块提供了以下几个模块级别的函数

电脑培训,计算机培训,平面设计培训,网页设计培训,美工培训,Web培训,Web前端开发培训

参数说明:
  • pattern: 一个正则表达式对象(可以通过compile函数获取)或一个表示正则表达式匹配模式的Python字符串,对于compile函数来说将pattern参数只能是字符串;

  • string: 需要用正则表达式来匹配的字符串对象

  • flags: 一个标志位,它会影响正则表达式对象的匹配行为,可取值下面会介绍;但是有一点需要说明的是,只有当pattern参数是字符串时才能指定这个flags参数,否则会报错;如果pattren参数是一个正则表达式对象,则flags参数需要在调用re.compile()函数时指定。

  • repl: sub和subn函数中的repl表示replacement,用于指定将匹配到的子串替换成什么内容,需要说明的是该参数的值可以是一个字符串,也可以是一个函数

  • count: sub和subn函数中的count表示最多可替换次数

  • maxsplit: split函数中的maxsplit蚕食表示最大分隔次数

说明: 通过对比会发现,上面这些re模块级别的函数除了re.compile、re.purge和re.escape这几个函数外,其它函数名都与正则表达式对象支持的方法同名。实际上re模块的这些函数都是对正则表达式对象相应方法的封装而已,功能是相同的。只是少了对pos和endpos参数的支持,但是我们可以手动通过字符串切片的方式来达到相应的需求。个人认为,我们应该尽可能的使用模块级别的函数,这样可以增强代码的兼容性。

3. 标志位flags

由上面的描述可知,flags参数在上面这些模块函数中是要个可选参数,re模块中预定了该参数可取的值:

电脑培训,计算机培训,平面设计培训,网页设计培训,美工培训,Web培训,Web前端开发培训

说明: 这些flag可以单独使用,也可以通过逻辑或操作符'|'进行拼接来联合使用。

二、使用re模块的步骤


我们有必要对re模块中所包含的类及其工作流程进行一下简单的、整体性的说明,这讲有利于我们对下面内容的理解。

1. 使用re模块进行正则匹配操作的步骤

  • 1)编写表示正则表达式规则的Python字符串str;

  • 2)通过re.compile()函数编译该Python字符串获得一个正则表达式对象(Pattern Object)p;

  • 3)通过正则表达式对象的p.match()或p.fullmatch()函数获取匹配结果--匹配对象(Match Object)m;

  • 4)通过判断匹配对象m是否为空可知是否匹配成功,也可以通过匹配对象m提供的方法获取匹配内容。

2. 使用re模块进行字串查找、字串替换和字符串分隔操作的步骤

  • 1)编写表示正则表达式规则的Python字符串str;

  • 2)通过re.compile()函数编译该Python字符串获得一个正则表达式对象(Pattern Object)p;

  • 3)通过正则表达式对象的p.search()或p.findall()或p.finditer()或p.sub()或p.subn()或p.split()函数完字串查找、字串替换和字符串分隔操作并获取相应的操作结果;

总结: 关于正则表达式的语法和编写示例请参考《正则表达式总结》)。根据上面的描述可知,将一个表示正则表达式的Python字符串编译成一个正则表达式对象是使用正则表达式完成相应功能的首要步骤,re模块中用于完成正则表达式编译功能的函数为re.compile()。

三、re模块简单应用示例


在上面的内容中,我们已经列出了re模块所提供的类,以及这些类的对象所支持的函数和属性,还有re模块所提供的模块级别的函数。这里我们要讨论的是怎样合理的使用这些方法和函数,换句话说就是在什么情况下使用这些方法和函数,以及如何使用的问题。我们之前说过,正则表达式主要可以用来提供以下几个功能:

  • 匹配测试

  • 子串/内容查找

  • 子串/内容替换

  • 字符串分割

下面我们就分别通过对这几个功能的实例实现对上面这些函数和方法以及参数和属性的使用做下说明和具体的解释。

1. 匹配测试

匹配测试,意思是通过特定的正则表达式对一个指定的字符串进行匹配来判断该字符串是否符合这个正则表达式所要求的格式。常见的使用场景举例:

  • 查看某个字符串(通常来自用户输入)是否是一个邮箱或电话号码

  • 用户注册时填写的用户名和密码是否符合指定需求

使用的函数或方法

通过re模块执行匹配测试时可以使用的函数或方法是:

  • re模块级别的match()和fullmatch()函数

  • 正则表达式对象的match()和fullmatch()方法

实例1

前面提到过,match()函数或方法只是匹配字符串的开始位置,而fullmatch匹配的是整个字符串;fullmatch()函数或方法就相当于给match()函数或方法的pattern或string参数加上行首边界元字符'^'和行尾边界元字符'$',下面来看个例子:

import re# 定义一个函数来对匹配结果进行展示 def display_match_obj(match_obj):
    if match_obj is None:
        print('Regex Match Fail!')    else:
        print('Regex Match Success!', match_obj)if __name__ == '__main__':
    p = re.compile(r'[a-z]+')
    display_match_obj(p.match('hello'))
    display_match_obj(p.match('hello123'))
    display_match_obj(p.fullmatch('hello'))
    display_match_obj(p.fullmatch('hello123'))
    
    display_match_obj(p.match('123hello'))
    display_match_obj(p.match('123hello', 3))

输出结果:

Regex Match Success! <_sre.SRE_Match object; span=(0, 5), match='hello'>Regex Match Success! <_sre.SRE_Match object; span=(0, 5), match='hello'>Regex Match Success! <_sre.SRE_Match object; span=(0, 5), match='hello'>Regex Match Fail!Regex Match Fail!Regex Match Success! <_sre.SRE_Match object; span=(3, 8), match='hello'>

分析:

  • '[a-z]+'能与'hello'和'hello123'的开头部分匹配

  • '[a-z]+'能与'hello'完全匹配

  • '[a-z]+'不能与'hello123'完全匹配

  • '[a-z]+'不能与'123hello'的开头部分匹配

  • '[a-z]+'能与'123hello'的切片'123hello'[3:]的开头部分匹配

实例2

上面使用的是正则表达式对象的match()和fullmatch()方法,我们也可以通过re提供的模块级别的函数来实现:

if __name__ == '__main__':
    p = re.compile(r'[a-z]+')
    display_match_obj(re.match(p, 'hello'))
    display_match_obj(re.match(p, 'hello123'))
    display_match_obj(re.fullmatch(p, 'hello'))
    display_match_obj(re.fullmatch(p, 'hello123'))
    
    display_match_obj(re.match(p, '123hello'))
    display_match_obj(re.match(p, '123hello'[3:]))  # 唯一不同的是这里

输出结果跟上面是一样的

re模块的match()和fullmatch()函数不支持pos和endpos参数,所以只能通过字符串切片先对字符串进行切割。

实例3

再来看个对比:

if __name__ == '__main__':
    display_match_obj(re.match(r'[a-z]+', 'hello123'))
    display_match_obj(re.match(r'[a-z]+$', 'hello123'))
    display_match_obj(re.match(r'^[a-z]+$', 'hello123'))

输出结果:

Regex Match Success! <_sre.SRE_Match object; span=(0, 5), match='hello'>Regex Match Fail!Regex Match Fail!

分析:

  • re.match()和re.fullmatch()中的pattern参数可以是正则表达式对象,也可以是Python字符串

  • re.match()函数中的正则表达式参数加上边界元字符'^'和'$'就相当于re.fullmatch()了

  • 当匹配过程是从字符串的第一个字符开始匹配时re.match(r'^[a-z]+$', 'hello123') 与 re.match(r'[a-z]+$', 'hello123')效果是一样的,因为re.match()本来就是从字符串开头开始匹配的;但是,如果匹配过程不是从字符串的第一个字符开始匹配时,它们是有区别的,具体请看下面这个例子。

实例4
if __name__ == '__main__':
    p1 = re.compile(r'[a-z]+$')
    p2 = re.compile(r'^[a-z]+$')
    display_match_obj(p1.match('123hello', 3))
    display_match_obj(p2.match('123hello', 3))

输出结果:

Regex Match Success! <_sre.SRE_Match object; span=(3, 8), match='hello'>Regex Match Fail!

这个很好理解,因为元字符'^'匹配的是表示字符串开始位置的特殊字符,而不是字符串内容的第一个字符。match()匹配的是字符串内容的第一个字符,因此即使是在MULTILINE模式,re.match()也将只匹配字符串的开始位置,而不是该字符串每一行的行首。

2. 内容查找

内容查找,意思是通过特定的正则表达式对一个指定的字符串的内容进行扫描来判断该字符串中是否包含与这个正则表达式相匹配的内容。

使用的函数或方法

通过re模块执行匹配测试时可以使用的函数或方法是:

  • re模块级别的search()、findall()和 finditer()函数

  • 正则表达式对象的search()、findall()和finditer()方法

实例1:search() vs match()

这个例子中,我们来看下search()函数的使用,以及它与match()函数的对比。
Python提供了两个不同的基于正则表达式的简单操作:

  • re.match(): 该函数仅是在字符串的开始位置进行匹配检测

  • re.search(): 该函数会在字符串的任意位置进行匹配检测

import re

string = 'abcdef'print(re.match(r'c', string))
print(re.search(r'c', string))

输出结果:

None<_sre.SRE_Match object; span=(2, 3), match='c'>

这个比较简单,不做过多解析。

我们应该能够想到,当正则表达式以'^'开头时,search()也会从字符串的开头进行匹配:

import re

string = 'abcdef'print(re.match(r'c', string))
print(re.search(r'^c', string))
print(re.search(r'^a', string))

输出结果:

NoneNone<_sre.SRE_Match object; span=(0, 1), match='a'>

这里再重复一下上面提到过的内容,就是元字符'^'与match()的从字符串开始位置开始匹配并不是完全等价的。因为元字符'^'匹配的是表示字符串开始位置的特殊字符,而不是字符串内容的第一个字符。match()匹配的是字符串内容的第一个字符,因此即使是在MULTILINE模式,re.match()也将只匹配字符串的开始位置,而不是该字符串每一行的行首;相关search('^...')却可以匹配每一行的行首。

下面来看个例子:

string = '''
A
B
X
'''print(re.match(r'X', string, re.MULTILINE))
print(re.search(r'^X', string, re.MULTILINE))

输出结果:

None<_sre.SRE_Match object; span=(5, 6), match='X'>
实例2:findall()与finditer()

findall()与finditer()也是用来查找一个字符串中与正则表达式相匹配的内容,但是从名字上就能看出来,findall()与finditer()会讲这个字符串中所有与正则表达式匹配的内容都找出来,而search()仅仅是找到第一个匹配的内容。另外findall()返回的是所有匹配到的字串所组成的列表,而finditer()返回的是一个迭代器对象,该迭代器对象会将每一次匹配到的结果都作为一个匹配对象返回。下面来看一个例子:

尝试找出一个字符串中的所有副词(英语的副词通常都是以字母‘ly’结尾):

import re

text = "He was carefully disguised but captured quickly by police."print(re.findall(r"\w+ly", text))

输出结果:

['carefully', 'quickly']

如果我们想要获取关于所有匹配内容的更多信息,而不仅仅是文本信息的话,就可以使用finditer()函数。finditer()可以提供与各个匹配内容相对应的匹配对象,然后我们就可以通过这个匹配对象的方法和属性来获取我们想要的信息。

我们来尝试获取一个字符串中所有副词以及它们各自在字符串中的切片位置:

import re

text = "He was carefully disguised but captured quickly by police."for m in re.finditer(r"\w+ly", text):
    print("%02d-%02d: %s" % (m.start(), m.end(), m.group()))

输出结果:

07-16: carefully40-47: quickly

3. 内容替换

传统的字符串操作只能替换明确指定的子串,而使用正则表达式默认是对一个字符串中所有与正则表达式相匹配的内容进行替换,也可以指定替换次数。

可使用的函数或方法
  • re模块的sub()和subn()函数

  • 正则表达式对象的sub()和subn()方法

sub函数返回的是被替换后的字符串,如果字符串中没有与正则表达式相匹配的内容,则返回原始字符串;subn函数除了返回被替换后的字符串,还会返回一个替换次数,它们是以元组的形式返回的。下面来看个例子:将一个字符串中的所有的'-'字符删除

import re

text = 'pro----gram-files'print(re.sub(r'-+', '', text))
print(re.subn(r'-+', '', text))

输出结果:

programfiles
('programfiles', 2)

说明: 被替换的内容(repl参数)可以是一个字符串,还可以是一个函数名。该函数会在每一次匹配时被调用,且该函数接收的唯一的参数是当次匹配相对应的匹配对象,通过这个函数我们可以一些逻辑更加复杂的替换。

比如,上面那个例子中,如果我们想要得到'program files'这个结果,我们就需要把多个'-'和当个'-'分别替换为 空字符串 和 一个空白字符:

def dashrepl(match_obj):
    if match_obj.group() == '-':        return ' '
    else:        return ''if __name__ == '__main__':
    text = 'pro----gram-files'
    print(re.sub(r'-+', dashrepl, text))
    print(re.subn(r'-+', dashrepl, text))

输出结果:

program files
('program files', 2)

说明: 当被替换的内容(repl参数)是一个字符串时可以使用'\1'、'\g<NAME>'来引用正则表达式中的捕获组所匹配到的内容,但是需要注意的是这个字符串必须带上r前缀。

下面来看个例子:把一个函数名前面加上'py_'前缀

p = r'def\s+([A-Za-z_]\w*)\s*\((?P<param>.*)\)'repl = r'def py_\1(\g<param>)'text = 'def myfunc(*args, **kwargs):'print(re.sub(p, repl, text))

输出结果:

def py_myfunc(*args, **kwargs):

4. 字符串分割

通过正则表达式对字符串进行分割的过程是:扫描整个字符串,查找与正则表达式匹配的内容,然后以该内容作为分割符对字符串进行分割,最终返回被分割后的子串列表。这对于将文本数据转换为Python易于读取和修改的结构化数据非常有用。

可以使用的函数或方法
  • re模块的split()函数

  • 正则表达式对象的split()方法

我们可以通过maxsplit参数来限制最大切割次数。

实例1:简单示例
print(re.split(r'\W+', 'Words, words, words.'))
print(re.split(r'\W+', 'Words, words, words.', 1))
print(re.split(r'[a-f]+', '0a3B9', flags=re.IGNORECASE))

输出结果:

['Words', 'words', 'words', '']
['Words', 'words, words.']
['0', '3', '9']

分析:

  • 第一行代码中,一共分割了3次(分隔符分别为:两个', '和一个'.'),因此返回的列表中有4个元素;

  • 第二行代码中,限制了最大分割次数为1,因此返回的列表中只有2个元素;

  • 第三行代码中,指定分隔符为一个或多个连续的小写字字母,但是指定的flag为忽略大小写,因此大写字母也可以作为分隔符使用;那么从小写字母'a'和大写字母'B'分别进行切割,所以返回的列表中有3个元素。

实例2:捕获组与匹配空字符串的正则表达式
  • 如果用于作为分割符的正则表达式包含捕获组,那么该捕获组所匹配的内容也会作为一个结果元素被返回;

  • 从Python 3.5开始,如果作为分隔符的正则表达式可以匹配一个空字符串,将会引发一个warning;

  • 从Python 3.5开始,如果作为分隔符的正则表达式只能匹配一个空字符串,那么它将会被拒绝使用并抛出异常。

http://www.cnblogs.com/yyds/p/6953348.html