Python正则表达式
简单介绍
知道[正则表达式]可能意味着用 3 步解决一个问题,而不是用 3000 步。如果你是一个技术怪侠,别忘了你用几次击键就能解决的问题,其他人需要数天的烦琐工作才能解决,而且他们容易犯错。
正则表达式英文名(regex)
由一个例子开始讲解基本方法
例子
>>> 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.')
>>> print('Phone number found: ' + mo.group())
Phone number found: 415-555-4242
解释:
虽然在 Python 中使用正则表达式有几个步骤,但每一步都相当简单。
1.用 import re 导入正则表达式模块。
2.用 re.compile()函数创建一个 Regex 对象(记得使用原始字符串)。
3.向 Regex 对象的 search()方法传入想查找的字符串。它返回一个 Match 对象。
4.调用 Match 对象的 group()方法,返回实际匹配文本的字符串。
search和findall()比较
除了search()外,还有一个findall()。
search()将返回一个Match对象,包含被查找字符串中的“第一次”匹配的文本,而 findall()方法将返回一组字符串,包含被查找字符串中的所有匹配。
比较
search()
>>> phoneNumRegex = re.compile(r'\d\d\d-\d\d\d-\d\d\d\d')
>>> mo = phoneNumRegex.search('Cell: 415-555-9999 Work: 212-555-0000')
>>> mo.group()
'415-555-9999'
findall()
>>> phoneNumRegex = re.compile(r'\d\d\d-\d\d\d-\d\d\d\d') # has no groups
>>> phoneNumRegex.findall('Cell: 415-555-9999 Work: 212-555-0000')
['415-555-9999', '212-555-0000']
作为 findall()方法的返回结果的总结,请记住下面两点:
1.如果调用在一个没有分组的正则表达式上,例如\d\d\d-\d\d\d-\d\d\d\d,方法
findall()将返回一个匹配字符串的列表,例如['415-555-9999', '212-555-0000']。
>>> phoneNumRegex = re.compile(r'\d\d\d-\d\d\d-\d\d\d\d') # has no groups
>>> phoneNumRegex.findall('Cell: 415-555-9999 Work: 212-555-0000')
['415-555-9999', '212-555-0000']
2.如果调用在一个有分组的正则表达式上,例如(\d\d\d)-(\d\d\d)-(\d\d\d\d),方
法 findall()将返回一个字符串的元组的列表(每个分组对应一个字符串),例如[('415',
'555', '1122'), ('212', '555', '0000')]
>>> phoneNumRegex = re.compile(r'(\d\d\d)-(\d\d\d)-(\d\d\d\d)') # has groups
>>> phoneNumRegex.findall('Cell: 415-555-9999 Work: 212-555-0000')
[('415', '555', '1122'), ('212', '555', '0000')]
sub()方法替换字符串
第一个参数用于取代发现的文本,第二个参数是那条长长的字符串
>>> namesRegex = re.compile(r'Agent \w+')
>>> namesRegex.sub('CENSORED', 'Agent Alice gave the secret documents to Agent Bob.')
'CENSORED gave the secret documents to CENSORED.'
方法总结:
compile() 创建一个用正则表达式的对象
search() 找到第一个匹配的
findall() 找到所有匹配的
group() 显示出匹配的
sub() 替换字符串
利用括号分组
(\d\d\d)-(\d\d\d-\d\d\d\d),让gruop中有更多的花样
>>> mo.group(1)
'415'
>>> mo.group(2)
'555-4242'
>>> mo.group(0)
'415-555-4242'
>>> mo.group()
'415-555-4242'
符号 ?* + {}
?匹配这个问号之前的分组零次或一次
>>> batRegex = re.compile(r'Bat(wo)?man')
>>> mo1 = batRegex.search('The Adventures of Batman')
>>> mo1.group()
'Batman'
>>> mo2 = batRegex.search('The Adventures of Batwoman')
>>> mo2.group()
'Batwoman'
*星号匹配零次或多次
>>> batRegex = re.compile(r'Bat(wo)*man')
>>> mo1 = batRegex.search('The Adventures of Batman')
>>> mo1.group()
'Batman'
>>> mo2 = batRegex.search('The Adventures of Batwoman')
>>> mo2.group()
'Batwoman'
>>> mo3 = batRegex.search('The Adventures of Batwowowowoman')
>>> mo3.group()
'Batwowowowoman'
+加号匹配一次或多次
>>> batRegex = re.compile(r'Bat(wo)+man')
>>> mo1 = batRegex.search('The Adventures of Batwoman')
>>> mo1.group()
'Batwoman'
>>> mo2 = batRegex.search('The Adventures of Batwowowowoman')
>>> mo2.group()
'Batwowowowoman'
>>> mo3 = batRegex.search('The Adventures of Batman')
>>> mo3 == None
True
{}花括号匹配特定次数
{n}匹配 n 次前面的分组。
{n,}匹配 n 次或更多前面的分组。
{,m}匹配零次到 m 次前面的分组。
{n,m}匹配至少 n 次、至多 m 次前面的分组。
{n,m}?或*?或+?对前面的分组进行非贪心匹配。
>>> haRegex = re.compile(r'(Ha){3}')
>>> mo1 = haRegex.search('HaHaHa')
>>> mo1.group()
'HaHaHa'
>>> mo2 = haRegex.search('Ha')
>>> mo2 == None
True
符号总结
?匹配零次或一次前面的分组。
匹配零次或多次前面的分组。
+匹配一次或多次前面的分组。
{n}匹配 n 次前面的分组。
{n,}匹配 n 次或更多前面的分组。
{,m}匹配零次到 m 次前面的分组。
{n,m}匹配至少 n 次、至多 m 次前面的分组。
{n,m}?或?或+?对前面的分组进行非贪心匹配。
字符分类
d,w,s,数字,字母,符号
\d 0 到 9 的任何数字
\D 除 0 到 9 的数字以外的任何字符
\w 任何字母、数字或下划线字符(可以认为是匹配“单词”字符)
\W 除字母、数字和下划线以外的任何字符
\s 空格、制表符或换行符(可以认为是匹配“空白”字符)
\S 除空格、制表符和换行符以外的任何字符
建立自己的字符分类
>>> vowelRegex = re.compile(r'[aeiouAEIOU]')
>>> vowelRegex.findall('RoboCop eats baby food. BABY FOOD.')
['o', 'o', 'o', 'e', 'a', 'a', 'o', 'o', 'A', 'O', 'O']
字符分类[a-zA-Z0-9]将匹配所有小写字母、大写字母和数字。
[]中一般的不怎么需要 \倒斜杠转义.、*、?或()字符
[^a]非a字符
通配字符 .
>>> atRegex = re.compile(r'.at')
>>> atRegex.findall('The cat in the hat sat on the flat mat.')
['cat', 'hat', 'sat', 'lat', 'mat']
点-星将匹配除换行外的所有字符。
通过传入 re.DOTALL 作为 re.compile()的第二个参数,可以让句点字符匹配所有字符,包括换行字符。
>>> newlineRegex = re.compile('.*', re.DOTALL)
>>> newlineRegex.search('Serve the public trust.\nProtect the innocent.\nUphold the law.').group()
'Serve the public trust.\nProtect the innocent.\nUphold the law.'
匹配时不区分大小写,可以向 re.compile()传入 re.IGNORECASE 或 re.I,作为第二个参数。
管理复杂的正则表达式
不是所有的正则表达式都很短的,向re.compile()中写入re.VERBOSE,可以忽略正则表达式字符串中的空白符和注释
原来
phoneRegex = re.compile(r'((\d{3}|\(\d{3}\))?(\s|-|\.)?\d{3}(\s|-|\.)\d{4}(\s*(ext|x|ext.)\s*\d{2,5})?)')
加入后
phoneRegex = re.compile(r'''(
(\d{3}|\(\d{3}\))? # area code
(\s|-|\.)? # separator
\d{3} # first 3 digits
(\s|-|\.) # separator
\d{4} # last 4 digits
(\s*(ext|x|ext.)\s*\d{2,5})? # extension
)''', re.VERBOSE)
compile的第二个参数总结
re.IGNORECASE 忽略大小写
re.VERBOSE 来编写注释
re.DOTALL 句点字符匹配换行
用管道符号可以一起传入,例如,既忽略大小写又句点字符匹配换行
>>> someRegexValue = re.compile('foo', re.IGNORECASE | re.DOTALL)
参考书籍
《Python编程快速上手》