Python 之正则表达式

这个坑是个伤心事啊,确实日常工作用到的比较少,所以总结一下加深记忆吧;

Regular Expression,在代码中常简写为regex、regexp或RE;正则表达式是对字符串操作的一种逻辑公式,用事先定义好的一些特定字符、及这些特定字符的组合,组成一个“规则字符串”,这个“规则字符串”用来表达对字符串的一种过滤逻辑。

正则表达式的特点有:
1. 灵活性、逻辑性和功能性非常强;
2. 可以迅速地用极简单的方式达到字符串的复杂控制。
3. 对于刚接触的人来说,比较晦涩难懂。
  •  创建和查找的基本步骤:
1.Python中所有正则表达式的函数都在re模块中,所以需要先 : import re
2.向re.compile中传入一个字符串值表示正则表达式,会返回一个Regex对象;
以电话号码为例子:
phonenumregex = re.compile(r'\d\d\d-\d\d\d-\d\d\d\d')
3.匹配Regex对象:Regex 对象的search()的方法查找传入的字符串,寻找正则表达式的匹配,如果没有找到姓应的匹配模式,则返回None;
如果找到匹配的对象,则返回一个Match对象。
mo=phonenumregex.search('My phone number is 415-555-4242.')
4.调用Match对象的group()方法,返回实际匹配的结果;
mo.group() 
 
note:向re.compile()中传入原始字符串,在字符串的第一个引号前面加上r,可以使该字符串标记为原始字符串,不包括转义字符,
即字符串中的\d即是数字的正则表达式,而无需加上转义字符\写成\\d
  • 利用括号分组(group方法)

可以添加括号在正则表达式中创建‘分组’

如电话号码分区号和号码两个部分:

(\d\d\d)-(\d\d\d-\d\d\d\d)

其中第一对括号是第一组,第二对括号是第二组,向group()匹配对象中传入1或2时则分别返回匹配的两组信息,传入0或者不传入参数,将返回真哥哥匹配的文本;

>>> import re
>>> phonenumregex = re.compile(r'(\d\d\d)-(\d\d\d-\d\d\d\d)')
>>> mo = phonenumregex.search('My number is 415-555-4242')
>>> mo.group()
'415-555-4242'
>>> mo.group(1)
'415'
>>> mo.group(2)
'555-4242'
>>> mo.groups()
('415', '555-4242')

一次获取所有分组,则可以使用groups()方法;

如果正则表达式需要匹配(),则应该使用转义字符\(和\) 表式括号;

  • 构造更复杂的正则表达式

1.管道 | 类似于或,匹配许多表达式中的一个:

>>> heroRegex = re.compile(r'Batman|Tina Fey')
>>> mo1 = heroRegex.search('Batman and Tina Fey')
>>> mo1.group()
'Batman'
>>> mo1 = heroRegex.search(' Tina Fey and Batman')
>>> mo1.group()
'Tina Fey'

 | 匹配返回第一次出现的匹配对象,如果需要匹配真正的|则需要使用转义字符 \| 来匹配 | ;

>>> batRegex=re.compile(r'Bat(man|mobile|copter|bat)')
>>> mo = batRegex.search('Batmobile lost a wheel')
>>> mo.group()
'Batmobile'
>>> mo.group(1)
'mobile'

2.问号实现可选匹配; 

?表示前面这个分组在这个模式中是可选的,如果有的话即进行匹配,?可以理解为匹配之前的分组零次或一次;

>>>betRegex = re.compile(r'(\d\d\d-)?\d\d\d-\d\d\d\d')
>>> mo1 = betRegex.search('My number is 415-555-4242')
>>> mo1.group()
'415-555-4242'
>>> mo2 = betRegex.search('My number is 555-4242')
>>> mo2.group()
'555-4242'

 3.用*匹配零次或多次

*之前的分组可以在文本中出现任意次;

betRegex = re.compile(r'Bat(wo)*man')
>>> mo1=betRegex.search('The Adventures of Batman')
>>> mo1.group()
'Batman'
>>> mo1=betRegex.search('The Adventures of Batwoman')
>>> mo1.group()
'Batwoman'
>>> mo1=betRegex.search('The Adventures of Batwowowowoman')
>>> mo1.group()
'Batwowowowoman'

 4.用+匹配一次或多次

+意味着匹配一次或多次,区别于*,+前面的分组必须至少出现一次;

>>> betRegex = re.compile(r'Bat(wo)+man')
>>> mo1=betRegex.search('The Adventures of Batwoman')
>>> mo1.group()
'Batwoman'
>>> mo1=betRegex.search('The Adventures of Batwowowowoman')
>>> mo1.group()
'Batwowowowoman'
>>> mo1=betRegex.search('The Adventures of Batman')
>>> mo1.group()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'NoneType' object has no attribute 'group'
>>> mo1 == None
True

 可以看到当wo一次都木有出现的时候,mo1返回的是None

5.用{}匹配出现的特定次数

在正则表示该分组之后加上{number}表示该分组匹配number次;

也可以在{}中指定一个范围,在{min,max}中可以限定出现的最大次数和最小次数;{min,}表示最少匹配min次,无上限;{,max}则表示最多匹配max次,无下限;

>>> haRegex = re.compile(r'(Ha){3}')
>>> mo1 = haRegex.search('HaHaHa')
>>> mo1.group()
'HaHaHa'
>>> mo1 = haRegex.search('HaHa')
>>> mo1.group()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'NoneType' object has no attribute 'group'
>>> mo1 == None
True

 可以看到因为{3}限定了匹配三次,所以HaHa匹配的结果是None。

6.贪心匹配和非贪心匹配

Python中的正则表达式默认是‘贪心’的,所以在有第二种选择的时候,会尽可能匹配最长的字符串;

{}的非贪心版本匹配尽可能最短的字符串,即在{}结束的后面加上一个问号;

>>> greedyRegex = re.compile(r'(Ha){3,5}')
>>> mo1 = greedyRegex.search('HaHaHaHaHa')
>>> mo1.group()
'HaHaHaHaHa'
>>> nongreedyRegex = re.compile(r'(Ha){3,5}?')
>>> mo1 = nongreedyRegex.search('HaHaHaHaHa')
>>> mo1.group()
'HaHaHa'
nongreedyRegex是使用正则表达式的非贪心模式,即{}?,所以可以看到即使min=3,max=5但是非贪心模式下匹配最短的字符串;
7.正则表达式的findall()方法
前面只介绍了search()方法,返回的是一个Match对象,仅仅包含被查字符串中的‘第一次’匹配文本;
findall()方法将返回一组字符串,包含被匹配字符串的所有匹配,返回的不是Match对象,而是一个字符串列表;
当正则表达式没有分组时,返回匹配结果的列表:
>>> phoneNumRegex = re.compile(r'\d\d\d-\d\d\d-\d\d\d\d')
>>> mo = phoneNumRegex.search('Cell:415-555-4242 Work 215-555-0000')
>>> mo.group()
'415-555-4242'
>>> mo = phoneNumRegex.findall('Cell:415-555-4242 Work 215-555-0000')
>>> mo.group()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'list' object has no attribute 'group'
>>> mo = phoneNumRegex.findall('Cell:415-555-4242 Work 215-555-0000')
>>> mo
['415-555-4242', '215-555-0000']
可以看到seach方法返回的结果需要调用group方法;
findall方法返回的是一个列表,所以调用group方法会报错,因为文本中出现了两次可匹配的文本,所以返回所有匹配结果的列表;
当正则表达式有分组时,返回匹配结果元组的列表:
>>> phoneNumRegex = re.compile(r'(\d\d\d)-(\d\d\d)-(\d\d\d\d)')
>>> mo = phoneNumRegex.findall('Cell:415-555-4242 Work 215-555-0000')
>>> mo
[('415', '555', '4242'), ('215', '555', '0000')]

匹配结果返回元组组成的列表,每个分组对应一个字符串,每完整匹配一次的结果是一个元组;

8.字符分类

\d 0到9的任何数字
\D 除0到9以外的任何字符
\w 任何字母,数字或者下划线字符(可以认为是匹配‘单词’字符)
\W 除字母,数字和下划线以外的字符
\s 空格,制表符或换行符(可以理解为匹配‘空白’字符)
\S 除空格,制表符和换行符之外的任何字符
 
 
 
 
 
 
 
字符分类[0-3]表示只匹配数字0-3;用\d{3}表示匹配一个阿拉伯数字三次;
>>> xmasregex = re.compile(r'\d+\s\w+')
>>> mo = xmasregex.findall('12 drummers,11 pipers,10 loards')
>>> mo
['12 drummers', '11 pipers', '10 loards']

善变的正则表达式中\d+表示匹配阿拉伯数字至少一次\s表示匹配空格字符\w+ 表示匹配字幕,数字或者下划线字符至少一次,所以\d+\s\w+要匹配的文本是有一个或者多个数字,然后跟着一个空白字符,接下来是一个或者多个字母/数字/下划线字符;

9.用[]建立自己的字符分类

因为字符分类的范围很广,所以如果只想匹配比如某几个阿拉伯数字或者某几个某几个字母符号,可以用[]建立所要的匹配的文本;

[a-zA-Z0-9]将匹配所有的大小写字符和阿拉伯数字;

[^aeio]将匹配到非后面的字符类;

[]内的匹配,普通的正则表达式符号不会被解释,所以[]内需要匹配./*?()等字符时,可以直接写入,不需要加上\进行注释;

10.^和$

在正则表达式开始处使用^,表明匹配必须发生在被匹配的文本开始处;
在正则表达式末尾处使用$,表明匹配的字符串必须以该模式结束;
同时使用^和$,则表示匹配的内容必须以某种开头结尾的模式才可以,只匹配某个子集是不可以的;
>>> Regex1 = re.compile(r'^\d')
>>> Regex1 = re.compile(r'^\d+$')
>>> mo=Regex1.search('1234567890').group()
>>> mo
'1234567890'
>>> mo=Regex1.search('123456xy7890').group()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'NoneType' object has no attribute 'group'
>>> Regex1.search('123456xy7890') == None
True

11.通配字符

.字符成为‘通配符’,可以匹配除了换行符之外的所有字符;

不过.只匹配一个字符,真正的句点则需要要用斜杠\转义;

>>> arRegex = re.compile(r'.at')
>>> arRegex.findall('The Cat in the hat sat on the flat mat.')
['Cat', 'hat', 'sat', 'lat', 'mat']

12.  .*匹配所有字符

.*可以匹配任意字符,使用贪心模式进行匹配尽可能多的文本;

.*?则是非贪心模式匹配尽可能短的文本;

>>> Regex1 = re.compile(r'<.*>')
>>> mo = Regex1.search('<To Serve man> for dinner.>')
>>> mo
<_sre.SRE_Match object; span=(0, 27), match='<To Serve man> for dinner.>'>
>>> mo.group()
'<To Serve man> for dinner.>'
>>> Regex2 = re.compile(r'<.*?>')
>>> mo = Regex2.search('<To Serve man> for dinner.>')
>>> mo.group()
'<To Serve man>'

13.传入第二个参数进行匹配

前面总结.句点可以匹配除换行以外的所有一个字符,通过传入re.DOTALL作为re.compile()的第二个参数,则此时可以让句点匹配所有字符,包括换行符;

如果只关心匹配字母,并不在乎哒谢谢,可通过传入re.IGNORECASE或者re.I作为re.compile的第二个参数,则针对[]或者直接列出的字母将忽略大小写进行匹配;

如果要匹配复杂的文本模式, 可能需要很长的正则表达式,此时可以传入re.VERBOSE作为re.compile的第二个参数,忽略正则表达式中的空白符,换行符和注释;

r加上三重引号可以输入多行字符串,和Python读取字符串一样;

猜你喜欢

转载自www.cnblogs.com/zahl/p/8909895.html