正则表达式通过特殊字符实现强大的文本匹配功能:普通字符(如字母、数字)直接匹配自身,
而元字符则具有特殊含义——点号(.
)匹配任意单个字符,星号(*
)表示前导字符零次或多次重复,
加号(+
)要求前导字符至少出现一次,问号(?
)使前导字符可选。
方括号[]
定义字符集合,匹配其中任意一个字符;反斜杠(\
)用于转义特殊字符,如\d
匹配数字。
圆括号()
创建捕获分组,竖线|
表示或逻辑,而花括号{}
精确控制重复次数。
此外,^
匹配字符串开头,$
匹配结尾,这些字符的组合使用能够构建出精确灵活的文本匹配模式,满足从简单查找复杂文本分析的各种需求。
在前面电话号码正则表达式的例子中,
了解了 \d
可以代表任何数字,
即 \d
是正则表达式(0|1|2|3|4|5|6|7|8|9)
的缩写。
实际上有许多这样的“缩写字符分类”。
常用字符分类的缩写代码
缩写字符分类 | 表示
\d
| 0到9的任何数字,\D
| 除0到9的数字以外的任何字符,\w
| 任何字母,数字或下划线字符,可以认为是匹配,单词,字符,\W
| 除字母,数字和下划线以外的任何字符,\s
| 空格,制表符或换行符,可以认为是匹配,空白,字符,\S
| 除空格,制表符和换行符以外的任何字符,
字符分类对于缩短正则表达式很有用。
字符分类[0-5]
只匹配数字0到5,
这比输入(0|1|2|3|4|5)
要短很多。
例如,在交互式环境中输入以下代码:
import re
xmasRegex = re.compile(r'\d+\s\w+')
xmasRegex.findall('12 drummers, 11 pipers, 10 lords, 9 ladies, 8 maids, 7 swans, 6 geese, 5 rings, 4 birds, 3 hens, 2 doves, 1 partridge')
['12 drummers', '11 pipers', '10 lords', '9 ladies', '8 maids', '7 swans', '6 geese', '5 rings', '4 birds', '3 hens', '2 doves', '1 partridge']
正则表达式 \d+\s\w+
匹配的文本有
一个或多个数字 (\d+)
,接下来是一个空白字符(\s)
,
接下来是一个或多个字母/数字/下划线字符(\w+)
。
findall()
方法将返回所有匹配该正则表达式的字符串,
放在一个列表中。
import re
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-ZO-9]
将匹配所有小写字母、大写字母和数字。
请注意,在方括号内,普通的正则表达式符号不会被解释。
这意味着不需要前面加上倒斜杠转义.
、
*
、?
或()
字符。例如,字符分类将匹配数字0到5和一个句点。
不需要将它写成[0-5\.]
。
通过在字符分类的左方括号后加上一个插入字符(^)
,
就可以得到“非字符类”。
非字符类将匹配不在这个字符类中的所有字符。
例如,在交互式环境中输入以下代码:
import re
vowelRegex = re.compile(r'[^aeiouAEIOU]')
print(vowelRegex.findall('RoboCop eats baby food. BABY FOOD.'))
['R', 'b', 'C', 'p', ' ', 't', 's', ' ', 'b', 'b', 'y', ' ', 'f', 'd', '.', ' ', 'B', 'B', 'Y', ' ', 'F', 'D', '.']
import re
beginsWithHello = re.compile(r'^Hello')
beginsWithHello.search('Hello world!')
<re.Match object; span=(0, 5), match='Hello'>
beginsWithHello.search('He said hello.') == None
True
正则表达式 r'\d$'
匹配以数字0到9结束的字符串。
在交互式环境中输入以下代码:
endsWithNumber = re.compile(r'\d$')
endsWithNumber.search('Your number is 42')
<re.Match object; span=(16, 17), match='2'>
endsWithNumber.search('Your number is forty two.') == None
True
正则表达式 r'^\d+$'
匹配从开始到结束都是数字的字符串。
在交互式环境中输入以下代码:
wholeStringlsNum = re.compile(r'^\d+$')
wholeStringlsNum.search('1234567890')
<re.Match object; span=(0, 10), match='1234567890'>
wholeStringlsNum.search('12345xyz67890') == None
True
wholeStringlsNum.search('12 34567890') == None
True
前面交互式脚本例子中的最后两次 search()
调用表明,
如果使用了 a
和 $
,
那么整个字符串必须匹配该正则表达式。
有时总是会混淆这两个符号的含义, 所以使用助记法 “Carrots cost dollars ”, 提醒插入符号在前面,美元符号在后面。
import re
atRegex = re.compile(r'.at')
atRegex.findall('The cat in the hat sat on the flat mat.')
['cat', 'hat', 'sat', 'lat', 'mat']
nameRegex = re.compile(r'First Name: (.*) Last Name: (.*)')
mo = nameRegex.search('First Name: A1 Last Name: Sweigart')
mo.group(1)
'A1'
mo.group(2)
'Sweigart'
点-星使用“贪心”模式:它总是匹配尽可能多的文本。 要用“非贪心”模式匹配所有文本,就使用点-星和问号。 像和大括号一起使用时那样,问号告诉Python用非贪心模式匹配。 在交互式环境中输入以下代码, 看看贪心模式和非贪心模式的区别:
nongreedyRegex = re.compile(r'<.*?>')
mo = nongreedyRegex.search('<To serve man> for dinner.>')
mo.group()
'<To serve man>'
greedyRegex = re.compile(r'<.*>')
mo = greedyRegex.search('<To serve man> for dinner.>')
mo.group()
'<To serve man> for dinner.>'
noNewlineRegex =re.compile('.*')
noNewlineRegex.search('Servethe public trust.\nProtect the innocent.\nUphold the law.').group()
'Servethe public trust.'
newlineRegex =re.compile('.*', re.DOTALL)
newlineRegex.search('Serve thepublic trust.\nProtect the innocent.\nUphold the law.').group()
'Serve thepublic trust.\nProtect the innocent.\nUphold the law.'
正则表达式 noNewlineRegex
在创建时没有向 re.compile()
传入 re.DOTALL
,它将匹配所有字符,直到第一个换行字符。但是,newlineRegex
在创建时向 re.compile()
传入了 re.DOTALL
,它将匹配所有字符。这就是为什么 newlineRegex.search()
调用匹配完整的字符串,包括其中的换行字符。