python学习笔记16:正则表达式

import re

1. re的method

1.1. re.match(pattern,string,flags=0)

说明:

  1. 从起始位置扫描string,返回match对象,match对象内容为第一个成功的匹配;(匹配不成功返回None)
  2. 与search的区别是,match要从开头扫描,search可以从任意位置开始扫描;
  3. 可能通过括号捕获内容,然后使用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)

说明:

  1. 扫描string,返回match对象,match对象内容为第一个成功的匹配;(匹配不成功返回None)
  2. 与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]) 替换

  1. 参数说明:
参数 说明
pattern 表示要匹配的pattern
repl 表示要将pattern替换成的目标字符串
string 原字符串
count 替换的个数, 默认所有匹配都替换
flags 是个什么东西?
  1. 基本替换:
>>> s0 = ‘hello 123 world 456’  
>>> s1 = re.sub(r‘\d+’, ‘222’, s0) # 将s0字符串中的数字串替换为‘222’;  
‘hello 222 world 222’  
  1. 逆向引用(在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是函数的情况".

  1. 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.  
  1. 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])

作用:

  1. start([group]):返回指定的组截获的子串在string中的起始索引(子串第一个字符的索引)。group默认值为0;
  2. end([group)):返回指定的组截获的子串在string中的结束索引(子串最后一个字符的索引+1)。group默认值为0;
  3. 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、\g引用分组,但不能使用编号0。\id与\g是等价的;但\10将被认为是第10个分组,如果你想表达\1之后是字符'0',只能使用\g<1>0。
例:

>>> 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. 贪婪匹配、非贪婪匹配

正则默认为贪婪匹配,进行非贪婪匹配需要使用?;

  1. *? 匹配前一个字符0次;
  2. +? 匹配前一个字符1次;
  3. ?? 匹配前一个字符0次;
  4. {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. 边界匹配

  1. ^ 匹配字符串开头(多行模式时匹配每一行开头);
  2. $ 匹配字符串末尾(多行模式时匹配每一行末尾);
  3. \A 仅匹配字符串开头;
  4. \Z 仅匹配字符串末尾;
  5. \b 匹配单词字符串(\w)边界;
  6. \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);
    说明1:分组做为一个整体,后面可以接数量词,表示这个分组出现多次;
    说明2:分组中可以使用表达式(|),这个“或”仅在该组中有效;
  2. (?P<name>…) :括起来的内容做为命名分组,作用类似于普通分组。
    区别是这个组还被指定了一个额外的名字;
  3. <number> :引用编号为<number>的分组;
  4. (?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’>
posted @ 2020-07-03 17:19  编程驴子  阅读(131)  评论(0编辑  收藏  举报