python学习笔记16:正则表达式
import re
1. re的method
1.1. re.match(pattern,string,flags=0)
说明:
- 从起始位置扫描string,返回match对象,match对象内容为第一个成功的匹配;(匹配不成功返回None)
- 与search的区别是,match要从开头扫描,search可以从任意位置开始扫描;
- 可能通过括号捕获内容,然后使用match对应.groups()/group()来访问捕获内容;
>>> re.match(r’a’, ‘abc’) # 匹配,因为a出现在了str的起始位置
>>>
>>> test = ‘abcd’
>>> if re.match(r'bc', test):
... print('ok')
... else:
... print('failed')
...
failed # bc没出现在test开头,所以匹配不成功;如果需要从非开始位置匹配,需要使用re.search.
1.2. re.search(pattern, string, flags=0)
说明:
- 扫描string,返回match对象,match对象内容为第一个成功的匹配;(匹配不成功返回None)
- 与match的区别是,match要从开头扫描,search可以从任意位置开始扫描;
>>> re.search(r’c\w+’, ‘abcde’) # 匹配,span=(2,5);即开始匹配的char idx为2,结束匹配的char idx为4;注意不包含char idx 5;
>>> re.search(r’c’, ‘abcdec’) # 匹配第一次出现的c,span=(2,3);注意:不会匹配第二次出现的c。
1.3. re.findall(pattern,string[, flags])
说明:搜索string,返回列表,列表内容为全部能匹配的子串或捕获括号内的字符;
没有捕获括号时,列表内容为匹配的子串
>>> f0 = re.findall(r'\d+', 'a00b01c02d03')
>>> f0 # 返回列表,注意:由于没有捕获括号,所以列表内容为匹配的子串
['00', '01', '02', '03']
pattern中有捕获括号,则findall返回的列表中是括号捕获的内容
>>> f0 = re.findall(r'12(3|4)?', 'a12b123c124d12') # pattern中有捕获括号;
>>> f0 # 返回列表,注意:由于pattern中有捕获括号,则findall返回的列表中是括号捕获的内容;
['', '3', '4', '']
如果需要返回pattern匹配的内容,需要使用非捕获括号
>>> f0 = re.findall(r'12(?:3|4)?', 'a12b123c124d12') # pattern中括号使用了?:,为非捕获括号;
>>> f0 #返回列表,注意:由于pattern中括号是非捕获模式,则findall返回的列表内容为匹配的子串;
['12', '123', '124', '12']
1.4. re.finditer(pattern, string[, flags])
说明:搜索string,返回迭代器,迭代器可以顺序访问每一个匹配结果(即一个match对象);
>>> # f0是一个<callable_iterator object at 0x…>
>>> f0 = re.finditer(r'12(?:3|4)?', 'a12b123c124d12')
>>> for e in f0:
... print(e.group(0)) # 注意,e是一个match对象。
...
12
123
124
12
1.5. re.split(pattern, sting[, maxsplit])
用正则表达式切分字符串,返回切分后的列表;
比使用固定字符(str.split(‘ ’))更灵活;
re.split(r’[\s\,\;]+’, ‘a, b; ; cd’) # [‘a’,’b’,’c’,’d’]
1.6. re.sub(pattern, repl, string[, count]) 替换
- 参数说明:
参数 | 说明 |
---|---|
pattern | 表示要匹配的pattern |
repl | 表示要将pattern替换成的目标字符串 |
string | 原字符串 |
count | 替换的个数, 默认所有匹配都替换 |
flags | 是个什么东西? |
- 基本替换:
>>> s0 = ‘hello 123 world 456’
>>> s1 = re.sub(r‘\d+’, ‘222’, s0) # 将s0字符串中的数字串替换为‘222’;
‘hello 222 world 222’
- 逆向引用(在pattern和repl中使用):
例1: 使用\1和\g<1>, 直接引用
>>> s0 = 'come dog, go dog'
>>> s1 = re.sub(r'come (\w+), go \1', '\g<1>', s0) # 返回‘dog’
'dog'
# 说明:\1 是pattern中对group(1)的逆向引用;
# 说明:\g<1>,是repl中对pattern group(1)的逆向引用;
例2: 使用\g
>>> s2 = re.sub(r’come (?P<name>\w+), go (?P=name)’, ‘\g<name>’, s0)
# 返回‘dog’,使用命名捕获,效果同s1
# 说明:(?P<name>xxx),是pattern中定义的命名组,name为组名,即原来的group(1)。
# 说明: (?P=name),是pattern中对命名组(name)的逆向引用。
# 说明:\g<name>,是repl中对命名组的逆向引用。
例3: 使用函数
前两种引用只能使用被引用的内容, 要想修改被引用的内容, 则replace需要是函数, 见下一节: "repl是函数的情况".
- repl是函数的情况:
>>> def _add1(m0):# 函数的参数是match对象
... str_new = ''
... for char in m0.group(‘num’): # match对象匹配的命名组(num)是字串,对它做循环
... #字符转为整数,然后加1,再转回字串,取最低位
... str_new += str(int(char)+1)[-1]
... return str_new
...
>>> s0 = ‘hello 123, world 456 696’
>>> s1 = re.sub(r’(?P<num>\d+)’, _add1, s0)
'hello 234, world 567 707' # s0中的数字每一位都加1, 9+1后取0.
- count,替换的次数, 默认所有匹配都替换
>>> #接上一个例子,要求只替换两次,第三个匹配不替换, 默认所有匹配都替换:
>>> s1 = re.sub(r’(?P<num>\d+)’, _add1, s0, 2)
'hello 234, world 567 696' # s0中的数字每一位都加1, 但第3个匹配不替换.
1.7. re.compile(pattern,flags=0)
re.compile()用于编译正则表达式模式字符串,并生成Regular Expression Objects;
如果正则表达式要重复使用几千次,可以预先编译该正则表达式,后续重复使用时就不需要编译了;
Regular Expression Objects可以使用上述的re的方法(match、search、findall等)。
>>> c0 = re.compile(r’(\d+)-(\d+)’) # 编译时不需要给匹配字符串;
>>> c0.match(‘010-123456’) # 返回match对象。调用时不需要给正则串;
>>> c0.match(‘010-123456’).group(2) # ‘123456’ , 第二个括号截获的内容;
>>> c0.match(‘abc010-123456def’) # None,使用match无法匹配,需要使用search。
>>>
>>> c0.search(‘abc010-123456def’) # 返回search对象。
>>> c0.search(‘abc010-123456def’).span(2) # (7, 13),第2个括号截获的子串的索引;
2. flags的可选值:
match、search等方法,有一个参数是flags,作用改变匹配的方式。
可用flags如下:
Flag值 | 作用 |
---|---|
0 | 默认值为0,不改变匹配方式 |
re.I(IGNORECASE) | 忽略大小写 |
re.M(MULTILINE) | 多行模式,改变^和$的行为 |
re.S(DOTALL) | 点可以匹配任意字符,包括换行符 |
re.L(LOCALE) | 做本地化识别的匹配,不推荐使用?? |
re.U(UNICODE) | 使用\w\W\s\S\d\D使用取决于unicode的定义的字符属性, python3默认使用该flag |
re.X(VERBOSE) | 冗长模式,该模式下patter字符串中可以是多行的,忽略其中的空白字符,且可以在其中添加注释 |
例子:
>>> re.match(r’a’, ‘A’, flags=0) # 正常匹配方式,大小写不同,不匹配,返回None;
>>> re.match(r’a’, ‘A’, flags=re.I) # 忽略大小写匹配,返回match对象;
>>> re.match(r’a B’, ‘AB’, flags=re.I) # patter中有空格,不匹配,返回None;
>>> re.match(r’a B’, ‘AB’, flags=re.I|re.X) # 忽略patter中的空白,匹配,返回match对象;
3. match对象的method
match对象通过调用这些方法,可以处理match匹配的内容。
3.1. group([group1, …])
作用:获取一个或多个分组截获的字符串;
例:
>>> m0 = re.match(r’^(\d+)-(\d+)’, ‘010-12345abc’) # 返回一个match对象
>>> m0.group(0) # ‘010-12345’,正则式匹配到的所有内容;
>>> m0.group(1) # ‘010’ ,第一个括号提取的内容;
>>> m0.group(2) # ‘12345’ ,第二个括号提取的内容;
>>> m0.group(1, 2) # (‘010’, ‘12345’) ,前2个括号提取的内容组成的元组;
>>> m0.group(3) # 报错:IndexError: no such group;
3.2. groups([default])
作用:以元组形式返回分部分组截获的字符串,相当于调用group(1,2, … last); default表示没有截获字符串的组以这个值替代,默认为None。
例:
>>> m0 = re.match(r’^(\d+)-(\d+)’, ‘010-12345abc’) # 返回一个match对象
>>> m0.groups() # (‘010’, ‘12345’),group(1)和group(2)组成的元组
3.3. groupdict([default])
作用:返回以有别名的组的别名为键、以该组截获的子串为值的字典,没有别名的组不包含在内。default含义同上;
例:暂无
3.4. start/end/span([group])
作用:
- start([group]):返回指定的组截获的子串在string中的起始索引(子串第一个字符的索引)。group默认值为0;
- end([group)):返回指定的组截获的子串在string中的结束索引(子串最后一个字符的索引+1)。group默认值为0;
- span([group]):返回(start(group), end(group));
例:
>>> m0 = re.search(r’(\d+)-(\d+)’, ‘abc010-123456def’)
>>> m0.group(0) # ‘010-123456’,正则式匹配到的内容;
>>> m0.start() # 3,group0截获的子串的起始idx;
>>> m0.end() # 13,group0截获的子串的结束idx+1;
>>> m0.span() # (3, 13),即(m0.start(),m0.end());
>>> m0.group(1) # ‘010’,第一个括号截取到的内容;
>>> m0.start(1) # 3,group1截获的子串的起始idx;
>>> m0.end(1) # 6,group1截获的子串的结束idx+1;
>>> m0.span(1) # (3, 6),即(m0.start(1), m0.end(1));
>>> m0.group(2) # ‘123456’,第2个括号截取到的内容;
>>> m0.start(2) # 7,group1截获的子串的起始idx;
>>> m0.end(2) # 13,group1截获的子串的结束idx+1;
>>> m0.span(2) # (7, 13),即(m0.start(2), m0.end(2));
>>> m0.group(3) # 报错,IndexError: no such group;
>>> m0.start(3) # 报错,IndexError: no such group;
>>> m0.end(3) # 报错,IndexError: no such group;
>>> m0.span(3) # 报错,IndexError: no such group;
3.5. expand([group])
作用:将匹配到的分组代入template中然后返回。template中可以使用\id或\g
例:
>>> m0 = re.search(r’(\d+)-(\d+)’, ‘abc010-123456def’)
>>> m0.expand(r’\2:\1’) # ‘123456:010’,将group()中的元素重新组合为:’grp2:grp1’
4. 字符
字符 | 说明 |
---|---|
一般字符 | 匹配自身; |
点号(.) | 匹配任意字符(换行符\n除外,在DOTALL模式也能匹配换行符); |
反斜杠() | 转义字符,改变字符原意,如匹配可以使用*或[],匹配\可以使用\; |
中括号([…]) | 字符集,可以匹配字符集中列出的任意一个字符; |
中括号的用法:
[abc] # 可以逐个列出
[a-c] # 可以给出范围:
[\w] # 可以使用预定义字符集:
[^abc] #可以取反,不是abc的其它字符
[\^ \- \[ \] ] # 特殊字符在字符集中要转义
5. 预定义字符集
字符集 | 说明 |
---|---|
\d | 数字,等价于[0-9]; |
\D | 非数字,等价于[^\d]; |
\s | 空白字符,等价于[<空格>\t\r\n\f\v]; |
\S | 非空白字符,等价于[^\s]; |
\w | 单词字符,等价于[_A-Za-z0-9]; |
\W | 非单词字符,等价于[^\w]; |
6. 数量词
表示匹配字符重复的次数。
数量词 | 说明 |
---|
- | 匹配前一个字符0~∞次;
- | 匹配前一个字符1~∞次;
? | 匹配前一个字符0~1次;
{m} | 匹配前一个字符m次;
{m,n}| 匹配前一个字符m~n次;
注意:量词可以作用在多个字符上,如:
>>> re.search(r’(123)+’, ‘a1123123123b’).group(0) # ‘123123123’,截取’123’重复多次;
>>> re.search(r’(123){2}’, ‘a1123123123b’).group(0) # ‘123123’,截取’123’重复2次;
7. 贪婪匹配、非贪婪匹配
正则默认为贪婪匹配,进行非贪婪匹配需要使用?;
- *? 匹配前一个字符0次;
- +? 匹配前一个字符1次;
- ?? 匹配前一个字符0次;
- {m,n}? 匹配前一个字符m次;
例1:
>>> re.search(r’a(\d*)’, ‘a1234b’).group(1) # ‘1234’,贪婪匹配,截取第一次出现的‘a+所有数字’;
>>> re.search(r’a(\d*?)’, ‘a1234b’).group(1) # ‘’,非贪婪匹配,截取第一次出现的‘a+第0个数字’;
>>> re.search(r’(\d*)’, ‘a1234b’).group(1) # ‘’,贪婪匹配,截取第一次出现的‘所有数字’;注意:这里用的\d*,会在idx==0时匹配上0个数字,所以无法截取到后面的’1234’。
例2
>>> re.search(r’(\d+)’, ‘a1234b’).group(1) # ‘1234’,贪婪匹配,截取第一次出现的所有数字;
>>> re.search(r’(\d+?)’, ‘a1234b’).group(1) # ‘1’,非贪婪匹配,截取第一次出现的第一个数字;
例3
>>> re.search(r’a(\d?)’, ‘a1234b’).group(1) # ‘1’,贪婪匹配,截取第一次出现的1个数字;
>>> re.search(r’(\d??)’, ‘a1234b’).group(1) # ‘’,非贪婪匹配,截取第一次出现的0个数字;
例4
>>> re.search(r’a(\d{1,3})’, ‘a1234b’).group(1) # ‘123’,贪婪匹配,截取第一次出现的n个数字;
>>> re.search(r’(\d{1,3}?)’, ‘a1234b’).group(1) # ‘1’,非贪婪匹配,截取第一次出现的1个数字;
8. 边界匹配
- ^ 匹配字符串开头(多行模式时匹配每一行开头);
- $ 匹配字符串末尾(多行模式时匹配每一行末尾);
- \A 仅匹配字符串开头;
- \Z 仅匹配字符串末尾;
- \b 匹配单词字符串(\w)边界;
- \B 相当于[^\b],非单词字符串边界;
9. 逻辑运算
只有“或”逻辑,没有“与”逻辑;
例1:不使用括号时,| 两边的字符做为整体来匹配,左边和右边出现任意一个即可。
>>> re.search(r’abc|def’, ‘abc’).group(0) # ‘abc’
>>> re.search(r’abc|def’, ‘def’).group(0) # ‘def’
>>> re.search(r’abc|def’, ‘abdef’).group(0) # ‘def’
例2:使用括号时,| 只作用到括号内,括号内左边和右边出现任意一个即可。
>>> re.search(r’ab(c|d)ef’, ‘abcef’).group(0) # ‘abcef’
>>> re.search(r’ab(c|d)ef’, ‘abdef’).group(0) # ‘abdef’
10. 分组、命名分组、引用分组
说明:
- (…) :括起来的内容作为普通分组,以数字编号(每遇到一个左括号,编号加1);
说明1:分组做为一个整体,后面可以接数量词,表示这个分组出现多次;
说明2:分组中可以使用表达式(|),这个“或”仅在该组中有效; - (?P<name>…) :括起来的内容做为命名分组,作用类似于普通分组。
区别是这个组还被指定了一个额外的名字; - <number> :引用编号为<number>的分组;
- (?P=name) :引用名称为<name>的分组;
例:
re.match(r’(\d)abc\1’, ‘9abc9’).group(1) # 返回9,普通分组,匹配成功。
在正则中使用\1向前引用,在match对象中使用group(1)访问;
re.match(r’(?P<idx>\d)abc(?P=idx)’, ‘9abc9’).group(‘idx’) # 返回9, 命名分组,成功。
在正则中使用(?P=idx)向前引用,在match对象中使用group(‘idx’)访问;
re.match(r’(?P<idx>\d)abc\1’, ‘9abc9’).group(1) # 同上,\1和group(1)仍然可以使用。
re.match(r’(?P<idx>\d)abc(?P=idx)’, ‘9abc9’).group(2) # IndexError, no such group。
注意:向前引用(?P=idx) 中有括号,但并不占据分组group(2);
s1 = re.sub(r'come (\w+), go \1', '\g<1>', ‘come dog, go dog’) # 返回dog
11. 特殊构造(构造中的括号不作为分组)
特殊构造 | 说明 |
---|---|
(?:…) | (…)的不分组/不捕获版本,这个地方的左括号不计入编号; 可以用于把多个字符做为一个整体使用‘|’或使用数量词,同时不影响其它分组的编号。如: re.match(r’(?:abc){2}(\d)’, ‘abcabc9’).group(1) # 返回9,abc的括号不占用group(1)。 |
(?iLmsux) | 用在pattern开头,用于指定匹配模式,可以使用iLmsux中的一个或多个; 如:re.match(r’(?i)(abc)’, ‘AbC’).group(1) # 返回AbC,pattern开头的(?i)表示忽略大小写;更多匹配模式见第9.2节。 |
(?#...) | #后的内容做为注释被忽略;如: re.match(r’abc(?#ignore)(123)’, ‘abc123’).group(1) # 返回123,(?#ignore)被忽略; |
x(?=…) | x之后必须有…才能匹配成功,但…不消耗字符串内容。如:re.match(r’a(?=\d)(\w+)’, ‘a1bc’).group(1) #返回1bc,a(?=\d)表示a后面要跟数字,但这个数字并没有在这里被匹配,而是被后面的(\w+)匹配,并放到了group(1)中。 |
x(?!...) | x之后必须没有…才能匹配成功,但…不消耗字符串内容。 |
(?<=…)x | x之前必须有…才能匹配成功,但…不消耗字符串内容。 |
(?<!...)x | x之前必须没有…才能匹配成功,但…不消耗字符串内容。 |
(?(id/name)yes-patt|no-patt) | 如果group(id/name)匹配到字符,则需要匹配yes_pattern,否则需要匹配no_pattern,|no_pattern可以省略。 |
12. 在正则表达式的patten中使用变量
>>> import re
>>> a = ‘1234’
>>> b = ‘1234567’
>>> re.match(f’{a}’, b)
<_sre.SRE_Mathc object; span=(0, 4), match=’1234’>