Python通过re模块提供对正则表达式的支持。使用re的一般步骤是先将正则表达式的字符串形式编译为Pattern实例,然后使用Pattern实例处理文本并获得匹配结果(一个Match实例),最后使用Match实例获得信息,进行其他的操作。
简介:
编译正则表达式模式,返回一个对象模式。(可以把那些常用的正则表达式编译成正则表达式对象,这样做的目的为了提高一点效率)
格式:
re.complie(pattern,flags=0)
pattern:编译时用的表达式字符串
flags:编译标志位,用于修改正则表达式的匹配方式,如是否区分大小写,多行匹配等等。
注释:
re.compile(‘pattern’, re.I | re.M) 与re.compile(’(?im)pattern’) 是等价的。
flag参数可选值有:
-简写- -全名- -注释- I IGNORECASE 忽略大小写 M MULTILINE 多行模式 S DOTALL 单选模式——点任意匹配模式 L LOCALE 使预定字符类 \w \W \b \B \s \S 取决于当前区域设定 U UNICODE 使预定字符类 \w \W \b \B \s \S \d \D 取决于unicode定义的字符属性 X VERBOSE 详细模式。该模式下正则表达式可以是多行,忽略空白字符,并可以加入注释 以下两个正则表达式是等价的:
a = re.compile(r"""\d + # the integral part \. # the decimal point \d * # some fractional digits""", re.X) b = re.compile(r"\d+\.\d*")
group()在正则表达式中用于获取分段截获的字符串,
代码解释如下:
import re
a = "123abc456"
print re.search("([0-9]*)([a-z]*)([0-9]*)",a).group(0) #123abc456,返回整体
print re.search("([0-9]*)([a-z]*)([0-9]*)",a).group(1) #123
print re.search("([0-9]*)([a-z]*)([0-9]*)",a).group(2) #abc
print re.search("([0-9]*)([a-z]*)([0-9]*)",a).group(3) #456
可以看出,正则表达式按照数字-字母-数字的顺序来获取相应字符串,那么分别就是“数字(group(1))–字母(group(2))–数字(group(3))”的对应关系,
其中,group(0)和group()效果相同,均为获取取得的字符串整体。
roup和groups是两个不同的函数。
一般,m.group(N) 返回第N组括号匹配的字符。
而m.group() == m.group(0) == 所有匹配的字符,与括号无关,这个是API规定的。
m.groups() 返回所有括号匹配的字符,以tuple格式。
a = "123abc456"
re.search("([0-9]*)([a-z]*)([0-9]*)", a).group()
'123abc456'
re.search("([0-9]*)([a-z]*)([0-9]*)", a).group(0)
'123abc456'
re.search("([0-9]*)([a-z]*)([0-9]*)", a).group(1)
'123'
re.search("([0-9]*)([a-z]*)([0-9]*)", a).group(2)
'abc'
re.search("([0-9]*)([a-z]*)([0-9]*)", a).group(3)
'456'
re.search("([0-9]*)([a-z]*)([0-9]*)", a).groups()
('123', 'abc', '456')
re.search("([0-9]*)([a-z]*)([0-9]*)", a).groups(1)
('123', 'abc', '456')
re.search("([0-9]*)([a-z]*)([0-9]*)", a).groups(2)
('123', 'abc', '456')
re.search("([0-9]*)([a-z]*)([0-9]*)", a).groups(3)
('123', 'abc', '456')
从以上例子中需掌握以下知识点:
总结:group就是返回捕获的内容。参数0或无参数表示整个正则表达式捕获的文本,1表示第1个括号匹配的内容,2表示第2个括号匹配的内容,以此类推。
当需要提取的内容只有一个,或是只需要获取第一次成功匹配的内容时,可以使用Match()方法。当使用Match()方法时,只要在某一位置匹配成功,就不再继续尝试匹配,并返回一个Match类型的对象。注意:Match只从位置0开始匹配,除非使用Pattern对象指定pos参数。在Pattern类的实例方法部分有详细说明。
... import re
... m=re.match(r'a','ababa')
... print(m.group())
a
虽然Match()只是取一次匹配,但是可以通过捕获组来获取多个指定子串。
# encoding: UTF-8
import re
m=re.match(r'(a)(b)','ababa')
print m.groups()
#结果为:('a', 'b')
Match对象是一次匹配的结果,包含了很多关于此次匹配的信息,可以使用Match提供的可读属性或方法来获取这些信息。
获得一个或多个分组截获的字符串;指定多个参数时将以元组形式返回。group1可以使用编号也可以使用别名;编号0代表整个匹配的子串;不填写参数时,返回group(0);没有截获字符串的组返回None;截获了多次的组返回最后一次截获的子串。
# encoding: UTF-8
import re
m=re.match(r'(\w{2})+','aabbcc')
print m.group(1)
#结果为:cc
再说明一下:search,找不到,则继续找,直到结束;如果一旦找到,则综止。而不管后面是否仍有内容匹配,都不在继续。
以元组形式返回全部分组截获的字符串。相当于调用group(1,2,…last)。default表示没有截获字符串的组以这个值替代,默认为None。
# encoding: UTF-8
... import re
... s="1-abc,2-abc,3-abc"
... pattern=r'((?P\d)-)(\w{3}),'
... m=re.search(pattern,s)
... print(m.groups())
('1-', '1', 'abc')
我们在来举个例子
>>> m=re.match("(\d+)\.(\d+)","23.123")
>>> m.groups()
('23', '123')
>>> m=re.match("(\d+)\.?(\d+)?","24") #这里的第二个\d没有匹配到,使用默认值"None"
>>> m.groups()
('24', None)
>>> m.groups("0")
('24', '0')
返回命名捕获组字典。以组名为键、以该组截获的子串为值,普通捕获组不包含在内。default含义同上。
>>> m=re.match("(\w+) (\w+)","hello world")
>>> m.groupdict()
{}
>>> m=re.match("(?P\w+) (?P\w+)" ,"hello world")
>>> m.groupdict()
{'secode': 'world', 'first': 'hello'}
通过上例可以看出,groupdict()对普通捕获组不起作用
返回指定的组截获的子串在string中的起始索引(子串第一个字符的索引)。group默认值为0。
返回指定的组截获的子串在string中的结束索引(子串最后一个字符的索引+1)。group默认值为0。
返回(start(group), end(group))。
将匹配到的分组代入template中然后返回。template中可以使用 \id 、\g 、\g 引用分组。id为捕获组的编号,name为命名捕获组的名字。
#encoding: UTF-8
# 结果为:a j j
import re
s="abcdefghijklmnopqrstuvwxyz"
pattern=r'(\w)(\w)(\w)(\w)(\w)(\w)(\w)(\w)(\w)(\w)(\w)(\w)(\w)'
m=re.search(pattern,s)
print m.expand(r'\1'), m.expand(r'\10'), m.expand(r'\g<10>')
对于字符串“a5”,是由两个字符“a”、“5”以及三个位置组成的,这一点对于正则表达式的匹配原理理解很重要。
import re
m = re.match(r'(\w+) (\w+)(?P.*)' , 'hello world!')
print "m.string:", m.string
print "m.re:", m.re
print "m.pos:", m.pos
print "m.endpos:", m.endpos
print "m.lastindex:", m.lastindex
print "m.lastgroup:", m.lastgroup
print "m.group(1,2):", m.group(1, 2)
print "m.groups():", m.groups()
print "m.groupdict():", m.groupdict()
print "m.start(2):", m.start(2)
print "m.end(2):", m.end(2)
print "m.span(2):", m.span(2)
print r"m.expand(r'\2 \1\3'):", m.expand(r'\2 \1\3')
### output ###
# m.string: hello world!
# m.re: <_sre.SRE_Pattern object at 0x016E1A38>
# m.pos: 0
# m.endpos: 12
# m.lastindex: 3
# m.lastgroup: sign
# m.group(1,2): ('hello', 'world')
# m.groups(): ('hello', 'world', '!')
# m.groupdict(): {'sign': '!'}
# m.start(2): 6
# m.end(2): 11
# m.span(2): (6, 11)
# m.expand(r'\2 \1\3'): world hello!
re.search()方法扫描整个字符串,并返回第一个成功的匹配。如果匹配失败,则返回None。
与re.match()方法不同,re.match()方法要求必须从字符串的开头进行匹配,如果字符串的开头不匹配,整个匹配就失败了;
re.search()并不要求必须从字符串的开头进行匹配,也就是说,正则表达式可以是字符串的一部分。
re.search(pattern, string, flags=0)
例1:
import re
content = 'Hello 123456789 Word_This is just a test 666 Test'
result = re.search('(\d+).*?(\d+).*', content)
print(result)
print(result.group()) # print(result.group(0)) 同样效果字符串
print(result.groups())
print(result.group(1))
print(result.group(2))
结果:
<_sre.SRE_Match object; span=(6, 49), match='123456789 Word_This is just a test 666 Test'>
123456789 Word_This is just a test 666 Test
('123456789', '666')
123456789
666
Process finished with exit code 0
例2:只匹配数字
import re
content = 'Hello 123456789 Word_This is just a test 666 Test'
result = re.search('(\d+)', content)
print(result)
print(result.group()) # print(result.group(0)) 同样效果字符串
print(result.groups())
print(result.group(1))
结果:
<_sre.SRE_Match object; span=(6, 15), match='123456789'>
123456789
('123456789',)
123456789
Process finished with exit code 0
匹配多个字符的相关格式,其实就是单个字符加上数量。注意下面数量的匹配都是针对前一个字符。
字符 | 功能 |
---|---|
* | 匹配前一个字符出现0次或者无限次,即可有可无 |
+ | 匹配前一个字符出现1次或者无限次,即至少有1次 |
? | 匹配前一个字符出现1次或者0次,即要么有1次,要么没有 |
{m} | 匹配前一个字符出现m次 |
{m,} | 匹配前一个字符至少出现m次 |
{m,n} | 匹配前一个字符出现从m到n次 |
让我们来看一个例子
# 导入re模块
import re
#1.匹配第一个是大写字母,第二个小写字母,后面只要是小写字母即可。
ret = re.match("[A-Z][a-z]*","Aafngsdfgnlsdf1224343")
print(ret.group()) #Aafngsdfgnlsdf
ret1 = re.match("[A-Z][a-z]*","AaAaaa34bbb")
print(ret1.group()) #Aa ,因为后面不是小写字母所以没匹配到。
#匹配下面字符串是否以字母或者下划线开头
ret = re.match("[a-zA-Z_]+[\w_]*","name1") #解释1:[a-zA-Z_]+字母下划线至少出现一次
print(ret.group()) #name1
ret = re.match("[a-zA-Z_]+[\w_]*","_name") #解释2:[\w_]*表示字母,数据下划线出现任意次
print(ret.group()) #_name
ret = re.match("[a-zA-Z_]+[\w_]*","2_name")
#print(ret.group()) #报错,因为匹配不上,返回None.
#3.匹配前面字符出现0次或者1次使用?
ret = re.match("[1-9]?[0-9]","7")
print(ret.group()) #7
ret = re.match("[1-9]?[0-9]","33")
print(ret.group()) #33
ret = re.match("[1-9]?[0-9]","09")
print(ret.group()) #0
#4.前面字符出现n此,或者m-n范围内的任意次
ret = re.match("[a-zA-Z0-9_]{6}","dsa2A9nfdsf")
print(ret.group()) #dsa2A9,匹配前6位是数字字符下划线即可
ret = re.match("[a-zA-Z0-9_]{3,8}","aSjsd239344")
ret1 = re.match("[a-zA-Z0-9_]{3,8}","aSjs")
print(ret1.group()) #aSjs,注意匹配前一个字符出现3-8次,只要这个范围内都算匹配成功,按实际匹配
print(ret.group()) #aSjsd239 匹配前一个字符出现3到8次
ret = re.match("[a-z0-9A-Z_]{3,}","a2")
print(ret.group()) #至少出现3次,所以如果只有两个的话,返回None,调用报错。
总结:单个字符匹配,多个字符匹配上面都已经演示过了,基本可以完成大多数字符串的匹配了。但是上面过于字符串的匹配都是从头开始匹配的,而实际开发中可能是从字符串中间,后者结尾开始匹配的。keep reading…
正则表达式对字符(单个字符)的表示:
字符 | 功能 |
---|---|
\W | 匹配非单词字符 |
\w | 匹配单词字符,即a-z、A-Z、0-9、_ |
\S | 匹配非空白 |
[ ] | 匹配[ ]中列举的字符,如果[a-zA-Z0-9],[a-zA-Z]表示所有字母和数字,后者表示所有字母,注意中间没有空格符号。 |
. | 匹配任意1个字符(除了\n),注意因为.表示任意一个字符,所以如果匹配‘.’则需要用转义字符.来表示 |
\d | 匹配数字,即0-9 |
\D | 匹配非数字,即不是数字 |
\s | 匹配空白,即 空格,tab键 |
让我们来看几个例子
# 导入re模块
import re
#1.测试.的使用,匹配任意字符开始的字符串
str='abc'
ret =re.match("..",str)
print(ret.group()) #ab.用两个..就表示只要str字符串开头是两个字符即可。
ret1 = re.match("....",str) #这种情况则会报错,因为str只有三个字符。
#2.匹配[]范围内的任意一个字符开头的字符串
str1 = "abcABC*?//"
str2 = "3afasdlfadsf"
ret2 = re.match("[a-z]",str1).group() #a
ret3 = re.match("[123456]",str2).group() #3,[1-6]等价[123456]
#3./d的使用,表示匹配任意一个数字
str3 = "第5名是我"
ret4 = re.match("第\d名",str3).group()
print(ret4) #第5名
ret4 = re.match("第[0-9]名",str3) #同样是表示0-9任意一个,[0-9]和\d效果一样
print(ret4.group()) #第5名
总结:注意上面对字符的匹配都是表示一个任意字符,或者某个范围内的任意一个字符,属于单个字符匹配。而实际开发中肯定都是用一个子串(多个字符)去匹配整个字符串。那么如何表示呢,请继续下去。
正则表达式对字符(字符集)的表示:
^ | 匹配字符串开头。在多行模式中匹配每一行的开头 | ^abc | abc |
---|---|---|---|
$ | 匹配字符串末尾,在多行模式中匹配每一行的末尾 | abc$ | abc |
| | 或。匹配|左右表达式任意一个,从左到右匹配,如果|没有包括在()中,则它的范围是整个正则表达式 | abc|def | abcdef |
{} | {m}匹配前一个字符m次,{m,n}匹配前一个字符m至n次,若省略n,则匹配m至无限次 | ab{1,2}c | abcabbc |
[] | 字符集。对应的位置可以是字符集中任意字符。字符集中的字符可以逐个列出,也可以给出范围,如[abc]或[a-c]。[^abc]表示取反,即非abc。 所有特殊字符在字符集中都失去其原有的特殊含义。用\反斜杠转义恢复特殊字符的特殊含义。 | a[bcd]e | abeaceade |
() | 被括起来的表达式将作为分组,从表达式左边开始没遇到一个分组的左括号“(”,编号+1. 分组表达式作为一个整体,可以后接数量词。表达式中的|仅在该组中有效。 | (abc){2} a(123|456)c | abcabca456c |
PyDev console: starting.
Python 3.8.2 (tags/v3.8.2:7b3ab59, Feb 25 2020, 23:03:10) [MSC v.1916 64 bit (AMD64)] on win32
import re
m=re.match('[cr][23][dp][o2]','c2po')
m.group()
'c2po'
Pattern对象是一个编译好的正则表达式,通过Pattern提供的一系列方法可以对文本进行匹配查找。
Pattern不能直接实例化,必须使用re.compile()进行构造。
我们来看几个例子:
import re
p = re.compile(r'(\w+) (\w+)(?P.*)' , re.DOTALL)
print "p.pattern:", p.pattern
print "p.flags:", p.flags
print "p.groups:", p.groups
print "p.groupindex:", p.groupindex
### output ###
# p.pattern: (\w+) (\w+)(?P.*)
# p.flags: 16
# p.groups: 3
# p.groupindex: {'sign': 3}
说明:匹配成功返回string,失败返回None
注意1:这个方法并不是完全匹配。当pattern结束时若string还有剩余字符,仍然视为成功。想要完全匹配,可以在表达式末尾加上边界匹配符’$’。
注意2:只要匹配成功就不再继续匹配。
比较下面两个例子:
例1:
# encoding: UTF-8
import re
pattern = re.compile(r'h')
match = pattern.match('hi,hello world!')
if match:
print match.group()
例2:
# encoding: UTF-8
import re
pattern = re.compile(r'e')
match = pattern.match('hi,hello world!')
if match:
print match.group()
说明:匹配成功返回string,失败返回None
这个方法用于查找字符串中可以匹配成功的子串。从string的pos下标处起尝试匹配pattern。如果pattern结束时仍可匹配,则返回一个Match对象(即如果找到一个匹配就返回一个MatchObject对象。而不会继续往下匹配。);若无法匹配,则将pos加1后重新尝试匹配;直到pos=endpos时仍无法匹配则返回None。
pos和endpos的默认值分别为0和len(string));re.search()无法指定这两个参数,参数flags用于编译pattern时指定匹配模式。
例1:
>>> m=re.search("abcd", '1abcd2abcd')
>>> m.group() #找到即返回一个match object,然后根据该对象的方法,查找匹配到的结果。
'abcd'
>>> m.start()
1
>>> m.end()
5
例2:
# encoding: UTF-8
import re
# 将正则表达式编译成Pattern对象
pattern = re.compile(r'world')
# 使用search()查找匹配的子串,不存在能匹配的子串时将返回None
# 这个例子中使用match()无法成功匹配
match = pattern.search('hello world!')
if match:
# 使用Match获得分组信息
print match.group()
### 输出 ###
# world
了解match方法与search方法的区别
match方法总是默认从字符串的起始位置开始匹配。就像模式字符串的第一个元素是/A一样。
如果关心发生在字符串的任意位置的匹配,而不是仅发生在字符串首部的匹配,Python管这种操作叫search,为是为了和match有所区别而取的新术语。
说明:返回list对象
按照能够匹配的子串做为分割符将string分割后返回列表。maxsplit用于指定最大分割次数,不指定将全部分割。
如果匹配失败,则返回仅有一个值的列表,该值为string字符串。】、
import re
p = re.compile(r'\d+') #测试匹配失败的结果,将 \d+ 改为下划线 _
print p.split('one1two2three3four4')
### output ###
# ['one', 'two', 'three', 'four', '']
说明:返回callable-iterator对象
搜索string,返回一个顺序访问每一个匹配结果(Match对象)的迭代器。
import re
p = re.compile(r'\d+')
for m in p.finditer('one1two2three3four4'):
print m.group(),
### output ###
# 1 2 3 4
说明:返回list对象
搜索string,以列表形式返回全部能匹配的子串。如果匹配失败,返回空列表**[]**
import re
p = re.compile(r'\d+')
print p.findall('one1two2three3four4')
### output ###
# ['1', '2', '3', '4']
说明:返回字符串对象,如果匹配失败,则返回原string
返回字符串string的一个拷贝,该串中的所有匹配均被替换成了repl。 当repl是一个字符串时,可以使用 \id 或\g 、\g 引用分组,但不能使用编号0。 当repl是一个方法时,这个方法应当只接受一个参数(Match对象),并返回一个字符串用于替换(返回的字符串中不能再引用分组)。 count默认为0,表示所有的匹配都被替换。如果count大于0时,只有前count个匹配被替换。
import re
p = re.compile(r'(\w+) (\w+)')
s = 'i say, hello world!'
print p.sub(r'\2 \1', s)
def func(m):
return m.group(1).title() + ' ' + m.group(2).title()
print p.sub(func, s)
### output ###
# say i, world hello!
# I Say, Hello World!
说明:返回tuple对象
subn 与 sub 相同, 只是 subn 返回一个元组 (new_string, n) ,这里 n 是替换的个数,如果匹配失败,返回tuple对象,new_string值为原字符串string,n值为0
import re
p = re.compile(r'(\w+) (\w+)')
s = 'i say, hello world!'
print p.subn(r'\2 \1', s)
def func(m):
return m.group(1).title() + ' ' + m.group(2).title()
print p.subn(func, s)
### output ###
# ('say i, world hello!', 2)
# ('I Say, Hello World!', 2)