Python正则表达式 简单介绍 由一个例子开始讲解基本方法 利用括号分组 符号 ?* + {} 字符分类 通配字符 . 管理复杂的正则表达式 参考书籍

知道[正则表达式]可能意味着用 3 步解决一个问题,而不是用 3000 步。如果你是一个技术怪侠,别忘了你用几次击键就能解决的问题,其他人需要数天的烦琐工作才能解决,而且他们容易犯错。

正则表达式英文名(regex)

由一个例子开始讲解基本方法

例子

>>> import re
>>> phoneNumRegex = re.compile(r'ddd-ddd-dddd')
>>> 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'ddd-ddd-dddd')
>>> mo = phoneNumRegex.search('Cell: 415-555-9999 Work: 212-555-0000')
>>> mo.group()
'415-555-9999'

findall()

>>> phoneNumRegex = re.compile(r'ddd-ddd-dddd') # has no groups
>>> phoneNumRegex.findall('Cell: 415-555-9999 Work: 212-555-0000')
['415-555-9999', '212-555-0000']

作为 findall()方法的返回结果的总结,请记住下面两点:
1.如果调用在一个没有分组的正则表达式上,例如ddd-ddd-dddd,方法
findall()将返回一个匹配字符串的列表,例如['415-555-9999', '212-555-0000']。

>>> phoneNumRegex = re.compile(r'ddd-ddd-dddd') # has no groups
>>> phoneNumRegex.findall('Cell: 415-555-9999 Work: 212-555-0000')
['415-555-9999', '212-555-0000']

2.如果调用在一个有分组的正则表达式上,例如(ddd)-(ddd)-(dddd),方
法 findall()将返回一个字符串的元组的列表(每个分组对应一个字符串),例如[('415',
'555', '1122'), ('212', '555', '0000')]

>>> phoneNumRegex = re.compile(r'(ddd)-(ddd)-(dddd)') # 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() 替换字符串

利用括号分组

(ddd)-(ddd-dddd),让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.
Protect the innocent.
Uphold the law.').group()
'Serve the public trust.
Protect the innocent.
Uphold 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编程快速上手》