日常的开发工作中,经常会有处理字符串的需求,简单的字符串处理,我们使用python内置的字符串处理函数就可以了,但是复杂的字符串匹配就需要借助正则表达式了。python是一门及其灵活的语言,在使用正则表达式的时候也是这样,这里集中介绍一下python中使用正则表达式来处理字符串。
首先简单介绍正则表达式的基础知识。正则表达式是独立于任何语言的一种字符串匹配表达式,任何编程语言都有处理正则表达式的能力。这里介绍的正则表达式的语法或者说是规则,对任何语言是通用的。
正则表达式基本语法:
元字符:
反义元字符:
量词:
懒惰限定符:
分组(小括号的使用):
我们已经提到了怎么重复单个字符(直接在字符后面加上限定符就行了);但如果想要重复多个字符又该怎么办?你可以用小括号来指定子表达式(也叫做分组),然后你就可以指定这个子表达式的重复次数了,你也可以对子表达式进行其它一些操作(后面会有介绍)。
(\d{1,3}.){3}\d{1,3}是一个简单的IP地址匹配表达式。要理解这个表达式,请按下列顺序分析它:\d{1,3}匹配1到3位的数字,(\d{1,3}.){3}匹配三位数字加上一个英文句号(这个整体也就是这个分组)重复3次,最后再加上一个一到三位的数字(\d{1,3})。
不幸的是,它也将匹配256.300.888.999这种不可能存在的IP地址。如果能使用算术比较的话,或许能简单地解决这个问题,但是正则表达式中并不提供关于数学的任何功能,所以只能使用冗长的分组,选择,字符类来描述一个正确的IP地址:((2[0-4]\d|25[0-5]|[01]?\d\d?).){3}(2[0-4]\d|25[0-5]|[01]?\d\d?)。
理解这个表达式的关键是理解2[0-4]\d|25[0-5]|[01]?\d\d?,这里我就不细说了,你自己应该能分析得出来它的意义。
IP地址中每个数字都不能大于255. 经常有人问我, 01.02.03.04 这样前面带有0的数字, 是不是正确的IP地址呢? 答案是: 是的, IP 地址里的数字可以包含有前导 0 (leading zeroes).
Python 自1.5版本起增加了re 模块,它提供 Perl 风格的正则表达式模式。
就其本质而言,正则表达式(或 RE)是一种小型的、高度专业化的编程语言, (在Python中)它内嵌在Python中,并通过 re 模块实现。正则表达式模式被 编译成一系列的字节码,然后由用 C 编写的匹配引擎执行。
re 模块使 Python 语言拥有全部的正则表达式功能。 python中有两种方式来使用re模块:
import re
import timeit
print(timeit.timeit(setup='''import re; reg = re.compile('<(?P\w*)>.*(?P=tagname)>')''' , stmt='''reg.match('xxx
')''', number=1000000))
print(timeit.timeit(setup='''import re''', stmt='''re.match('<(?P\w*)>.*(?P=tagname)>', 'xxx
')''' , number=1000000))
0.4837358
1.1406693
可以看到第一种方式,也就是使用compile函数生成正则表达式对象的方式执行的效率更高。
上面的例子使用了python的性能测试模块timeit,关于timeit模块的使用可以参考:python性能分析(一) | timeit模块
下面分别介绍两种匹配方式的使用。
尝试从字符串的起始位置匹配一个模式,匹配成功的话返回一个匹配的对象,如果不是起始位置匹配成功的话,match()就返回none。
参数:
调用re的匹配方法后,返回匹配对象SRE_MATHCH,匹配对象调用group函数可以返回匹配的内容。
matchobj.group():返回正则表达式整体匹配到的结果,也可以写成matchobj.group(0)
如果正则表示式中含有分组使用,传入分组的下标可以返回特定分组匹配到的字符串,分组小标从1开始。如果模式串多次匹配,group将返回最后一次匹配。
matchobj.group(1): 返回第一个分组匹配到的内容。
matchobj.group(1,2) : 返回第一个,第二个分组匹配到的内容,以列表的形式返回
matchobj.groups(): 返回所有分组匹配到的内容,构成一个列表。
matchobj.groupdict(): 如果分组有命名的话,返回分组命名和分组匹配到的内容构成的字符串。
matchobj.start(): 用于获取分组匹配的子串在整个字符串中的起始位置(子串第一个字符的索引),参数默认值为 0
matchobj.end(): 用于获取分组匹配的子串在整个字符串中的结束位置(子串最后一个字符的索引+1),参数默认值为 0
matchobj.span(): 返回((start(), end()))
import re
line = "Cats are smarter than dogs"
matchObj = re.match(r'(.*) are (.*?) .*', line, re.M | re.I)
if matchObj:
g = matchObj.group()
print "matchObj.groups() : ", matchObj.groups()
print "matchObj.group() : ", matchObj.group()
print "matchObj.group(0) : ", matchObj.group(0)
print "matchObj.group(1) : ", matchObj.group(1)
print "start(0):", matchObj.start(1)
print "end(0):", matchObj.end(1)
print "span(0):", matchObj.span(1)
print "matchObj.group(2) : ", matchObj.group(2)
# print "matchObj.group(2) : ", matchObj.group(3)
# print "matchObj.group(2) : ", matchObj.group(4)
print "groupdict.group() : ", matchObj.groupdict('(.*)')
else:
print "No match!!"
扫描整个字符串并返回第一个成功的匹配。匹配成功re.search方法返回一个匹配的对象,否则返回None。我们可以使用group(num) 或 groups() 匹配对象函数来获取匹配表达式。
参数:
import re
line = "Cats are smarter than dogs";
matchObj = re.match(r'dogs', line, re.M | re.I)
if matchObj:
print "match --> matchObj.group() : ", matchObj.group()
else:
print "No match!!"
matchObj = re.search(r'dogs', line, re.M | re.I)
if matchObj:
print "search --> matchObj.group() : ", matchObj.group()
else:
print "No match!!"
No match!!
search --> matchObj.group() : dogs
re.match只匹配字符串的开始,如果字符串开始不符合正则表达式,则匹配失败,函数返回None;而re.search匹配整个字符串,直到找到一个匹配。
通过正则表达式将字符串分离。如果用括号将正则表达式括起来,那么匹配的字符串也会被列入到list中返回。maxsplit是分离的次数,maxsplit=1分离一次,默认为0,不限制次数。
参数:
import re
print(re.split('\W+', 'runoob, runoob, runoob.'))
print(re.split('(\W+)', ' runoob, runoob, runoob.'))
print(re.split('\W+', ' runoob, runoob, runoob.', 1))
print(re.split('a*', 'hello world')) # 对于一个找不到匹配的字符串而言,split 不会对其作出分割
['runoob', 'runoob', 'runoob', '']
['', ' ', 'runoob', ', ', 'runoob', ', ', 'runoob', '.', '']
['', 'runoob, runoob, runoob.']
['hello world']
用于替换字符串中的匹配项。
import re
phone = "2004-959-559 # 这是一个国外电话号码"
# 删除字符串中的 Python注释
num = re.sub(r'#.*$', "", phone)
print "电话号码是: ", num
# 删除非数字(-)的字符串
num = re.sub(r'\D', "", phone)
print "电话号码是 : ", num
电话号码是: 2004-959-559
电话号码是 : 2004959559
在字符串中找到正则表达式所匹配的所有子串,并返回一个列表,如果没有找到匹配的,则返回空列表。
注意: match 和 search 是匹配一次,findall 匹配所有。
import re
pattern = re.compile(r'\d+') # 查找数字
result = re.findall(r'\d+','runoob 123 google 456')
print(result)
['123', '456']
和 findall 类似,在字符串中找到正则表达式所匹配的所有子串,并把它们作为一个迭代器返回。
参数:
import re
it = re.finditer(r"\d+", "12a32bc43jf3")
print(type(it))
for match in it:
print (match.group())
12
32
43
3
上面介绍的所有的匹配的方法都可以有这种形式对应的函数,即先将正则表达式编译成一个正则表达式对象,再使用该对象调用相应的函数,进行匹配,匹配返回的结果和使用re模块调用相应的函数一致。
需要注意的一点是,使用compile编译这种形式对应的匹配函数多了两个可选的参数,即可以指定匹配的开始和结束位置。
用于编译正则表达式,生成一个正则表达式( Pattern )对象,供 match() 和 search(),findall()等匹配函数使用。
参数:
尝试从字符串的起始位置匹配一个模式(如果指定了范围的话,是指定位置的起始位置),匹配成功的话返回一个匹配的对象,如果不是起始位置匹配成功的话,match()就返回none。
参数:
import re
pattern = re.compile(r'\d+') # 用于匹配至少一个数字
m = pattern.match('one12twothree34four') # 查找头部,没有匹配
print m
m = pattern.match('one12twothree34four', 2, 10) # 从'e'的位置开始匹配,没有匹配
print m
m = pattern.match('one12twothree34four', 3, 10) # 从'1'的位置开始匹配,正好匹配
print m # 返回一个 Match 对象
print m.group(0) # 可省略 0
print m.groups()
print m.start(0) # 可省略 0
print m.end(0) # 可省略 0
print m.span(0) # 可省略 0
None
None
<_sre.SRE_Match object at 0x000000000389D780>
12
()
3
5
(3, 5)
在字符串中找到正则表达式所匹配的所有子串,并返回一个列表,如果没有找到匹配的,则返回空列表。
注意: match 和 search 是匹配一次,findall 匹配所有。
参数:
import re
pattern = re.compile(r'\d+') # 查找数字
result1 = pattern.findall('runoob 123 google 456')
result2 = pattern.findall('run88oob123google456', 0, 10)
print(result1)
print(result2)
['123', '456']
['88', '12']
说明: 其他的函数用法与re直接调用的同名函数的用法相同,只是多了两个指定开始位置和结束位置的参数,不做过多的介绍。
参考链接:
- Python中的正则表达式
- 详解Python中的正则表达式
- re库中group(), groups(), groupdict() 用法
- Python: str.split()和re.split()的区别
- Python:re中的group方法简介
- python3进阶之正则表达式之re模块之分组(group)、贪心匹配、编译
- python之re模块详解