正则表达式
正则表达式(缩写为 regex [...])是一系列字符,用于在文本中指定搜索模式。[...] 用于字符串搜索算法,对字符串执行“查找”或“查找和替换”操作,或用于输入验证。
- 使用
import re导入 regex 模块。 - 使用
re.compile()函数创建 Regex 对象。(记住使用原始字符串。) - 将要搜索的字符串传递给 Regex 对象的
search()方法。这将返回一个Match对象。 - 调用 Match 对象的
group()方法以返回实际匹配文本的字符串。
Python 中所有的 regex 函数都在 re 模块中:
# Import re module for regular expression operations
import re
Regex 符号
| 符号 | 匹配内容 |
|---|---|
? | 前一个组的零个或一个。 |
* | 前一个组的零个或多个。 |
+ | 前一个组的一个或多个。 |
{n} | 前一个组的正好 n 个。 |
{n,} | 前一个组的 n 个或更多。 |
{,m} | 前一个组的 0 到 m 个。 |
{n,m} | 前一个组的至少 n 个且至多 m 个。 |
{n,m}? 或 *? 或 +? | 对前一个组执行非贪婪匹配。 |
^spam | 意味着字符串必须以 spam 开头。 |
spam$ | 意味着字符串必须以 spam 结尾。 |
. | 任何字符,换行符除外。 |
\d, \w, 和 \s | 分别表示数字、单词或空格字符。 |
\D, \W, 和 \S | 分别表示非数字、非单词或非空格的任何字符。 |
[abc] | 方括号之间的任何字符(如 a、b、 )。 |
[^abc] | 方括号之间以外的任何字符。 |
匹配 regex 对象
# re.compile(): create regex pattern object (use raw string r'' to avoid escaping)
phone_num_regex = re.compile(r'\d\d\d-\d\d\d-\d\d\d\d') # Pattern: 3 digits-3 digits-4 digits
mo = phone_num_regex.search('My number is 415-555-4242.') # Search for pattern
print(f'Phone number found: {mo.group()}') # group() returns matched text
Phone number found: 415-555-4242
使用圆括号进行分组
# Parentheses create groups: group(1) returns first group, group(2) returns second
phone_num_regex = re.compile(r'(\d\d\d)-(\d\d\d-\d\d\d\d)') # Two groups in parentheses
mo = phone_num_regex.search('My number is 415-555-4242.')
mo.group(1) # Returns first group: '415'
'415'
mo.group(2)
'555-4242'
mo.group(0)
'415-555-4242'
mo.group()
'415-555-4242'
登录后即可答题并追踪学习进度
group() 时,它返回什么?要一次性检索所有组,请使用 groups() 方法:
# groups(): returns tuple of all groups
mo.groups() # Returns ('415', '555-4242')
('415', '555-4242')
area_code, main_number = mo.groups()
print(area_code)
415
print(main_number)
555-4242
使用管道进行多重分组
您可以在任何想要匹配多个表达式中的一个的地方使用 | 字符。
hero_regex = re.compile (r'Batman|Tina Fey')
mo1 = hero_regex.search('Batman and Tina Fey.')
mo1.group()
'Batman'
mo2 = hero_regex.search('Tina Fey and Batman.')
mo2.group()
'Tina Fey'
您也可以在正则表达式中使用管道来匹配多个模式中的一个:
bat_regex = re.compile(r'Bat(man|mobile|copter|bat)')
mo = bat_regex.search('Batmobile lost a wheel')
mo.group()
'Batmobile'
mo.group(1)
'mobile'
使用问号进行可选匹配
? 字符将它前面的组标记为模式的可选部分。
bat_regex = re.compile(r'Bat(wo)?man')
mo1 = bat_regex.search('The Adventures of Batman')
mo1.group()
'Batman'
mo2 = bat_regex.search('The Adventures of Batwoman')
mo2.group()
'Batwoman'
使用星号匹配零个或多个
*(星号)表示“匹配零个或多个”。位于星号之前的组可以在文本中出现任意次数。
bat_regex = re.compile(r'Bat(wo)*man')
mo1 = bat_regex.search('The Adventures of Batman')
mo1.group()
'Batman'
mo2 = bat_regex.search('The Adventures of Batwoman')
mo2.group()
'Batwoman'
mo3 = bat_regex.search('The Adventures of Batwowowowoman')
mo3.group()
'Batwowowowoman'
使用加号匹配一个或多个
+(加号)表示匹配一个或多个。加号前面的组必须至少出现一次:
bat_regex = re.compile(r'Bat(wo)+man')
mo1 = bat_regex.search('The Adventures of Batwoman')
mo1.group()
'Batwoman'
mo2 = bat_regex.search('The Adventures of Batwowowowoman')
mo2.group()
'Batwowowowoman'
mo3 = bat_regex.search('The Adventures of Batman')
mo3 is None
True
使用花括号匹配特定重复次数
如果你有一个你想重复特定次数的组,在你的正则表达式中跟在组后面加上花括号中的数字:
ha_regex = re.compile(r'(Ha){3}')
mo1 = ha_regex.search('HaHaHa')
mo1.group()
'HaHaHa'
mo2 = ha_regex.search('Ha')
mo2 is None
True
你可以用花括号之间的最小和最大值来指定一个范围,而不是一个数字。例如,正则表达式 (Ha){3,5} 将匹配 ‘HaHaHa’、‘HaHaHaHa’ 和 ‘HaHaHaHaHa’。
ha_regex = re.compile(r'(Ha){2,3}')
mo1 = ha_regex.search('HaHaHaHa')
mo1.group()
'HaHaHa'
贪婪匹配和非贪婪匹配
Python 的正则表达式默认是贪婪的:在有歧义的情况下,它们会匹配尽可能长的字符串。花括号的非贪婪版本(匹配最短字符串)在闭合花括号后跟一个问号。
greedy_ha_regex = re.compile(r'(Ha){3,5}')
mo1 = greedy_ha_regex.search('HaHaHaHaHa')
mo1.group()
'HaHaHaHaHa'
non_greedy_ha_regex = re.compile(r'(Ha){3,5}?')
mo2 = non_greedy_ha_regex.search('HaHaHaHaHa')
mo2.group()
'HaHaHa'
登录后即可答题并追踪学习进度
_ 而不是 +? (例如 _?, +?, {3,5}?)findall() 方法
findall() 方法将返回搜索字符串中所有匹配项的字符串列表。
phone_num_regex = re.compile(r'\d\d\d-\d\d\d-\d\d\d\d') # has no groups
phone_num_regex.findall('Cell: 415-555-9999 Work: 212-555-0000')
['415-555-9999', '212-555-0000']
创建自定义字符类
您可以使用方括号定义自己的字符类。例如,字符类 [aeiouAEIOU] 将匹配任何元音字母,包括大小写。
vowel_regex = re.compile(r'[aeiouAEIOU]')
vowel_regex.findall('Robocop eats baby food. BABY FOOD.')
['o', 'o', 'o', 'e', 'a', 'a', 'o', 'o', 'A', 'O', 'O']
您也可以通过使用连字符包含字母或数字的范围。例如,字符类 [a-zA-Z0-9] 将匹配所有小写字母、大写字母和数字。
通过在字符类的开方括号后放置一个脱字符号 (^),您可以创建一个负字符类,它将匹配字符类中不包含的所有字符:
consonant_regex = re.compile(r'[^aeiouAEIOU]')
consonant_regex.findall('Robocop eats baby food. BABY FOOD.')
['R', 'b', 'c', 'p', ' ', 't', 's', ' ', 'b', 'b', 'y', ' ', 'f', 'd', '.', ' ', 'B', 'B', 'Y', ' ', 'F', 'D', '.']
脱字符号和美元符号字符
您也可以在正则表达式的开头使用脱字符号
^来指示匹配必须发生在搜索文本的开头。同样,您可以在正则表达式的末尾放置一个美元符号
$来指示字符串必须以该正则表达式模式结尾。您可以同时使用
^和$来指示整个字符串必须匹配该正则表达式。
正则表达式字符串 r'^Hello' 匹配以 ‘Hello’ 开头的字符串:
begins_with_hello = re.compile(r'^Hello')
begins_with_hello.search('Hello world!')
<_sre.SRE_Match object; span=(0, 5), match='Hello'>
begins_with_hello.search('He said hello.') is None
True
正则表达式字符串 r'\d\$' 匹配以 0 到 9 的数字字符结尾的字符串:
whole_string_is_num = re.compile(r'^\d+$')
whole_string_is_num.search('1234567890')
<_sre.SRE_Match object; span=(0, 10), match='1234567890'>
whole_string_is_num.search('12345xyz67890') is None
True
whole_string_is_num.search('12 34567890') is None
True
通配符字符
正则表达式中的 .(点)字符将匹配除换行符外的任何字符:
at_regex = re.compile(r'.at')
at_regex.findall('The cat in the hat sat on the flat mat.')
['cat', 'hat', 'sat', 'lat', 'mat']
使用点星号匹配所有内容
name_regex = re.compile(r'First Name: (.*) Last Name: (.*)')
mo = name_regex.search('First Name: Al Last Name: Sweigart')
mo.group(1)
'Al'
mo.group(2)
'Sweigart'
.* 默认使用贪婪模式:它总是会尝试匹配尽可能多的文本。要以非贪婪方式匹配任何和所有文本,请使用点、星号和问号 (.*?)。问号告诉 Python 以非贪婪方式匹配:
non_greedy_regex = re.compile(r'<.*?>')
mo = non_greedy_regex.search('<To serve man> for dinner.>')
mo.group()
'<To serve man>'
greedy_regex = re.compile(r'<.*>')
mo = greedy_regex.search('<To serve man> for dinner.>')
mo.group()
'<To serve man> for dinner.>'
使用点字符匹配换行符
点星号会匹配除换行符外的所有内容。通过将 re.DOTALL 作为第二个参数传递给 re.compile(),您可以使点字符匹配所有字符,包括换行符:
no_newline_regex = re.compile('.*')
no_newline_regex.search('Serve the public trust.\nProtect the innocent.\nUphold the law.').group()
'Serve the public trust.'
newline_regex = re.compile('.*', re.DOTALL)
newline_regex.search('Serve the public trust.\nProtect the innocent.\nUphold the law.').group()
'Serve the public trust.\nProtect the innocent.\nUphold the law.'
忽略大小写匹配
要使正则表达式忽略大小写,您可以将 re.IGNORECASE 或 re.I 作为第二个参数传递给 re.compile():
robocop = re.compile(r'robocop', re.I)
robocop.search('Robocop is part man, part machine, all cop.').group()
'Robocop'
robocop.search('ROBOCOP protects the innocent.').group()
'ROBOCOP'
robocop.search('Al, why does your programming book talk about robocop so much?').group()
'robocop'
使用 sub() 方法替换字符串
Regex 对象的 sub() 方法接收两个参数:
- 第一个参数是要替换任何匹配项的字符串。
- 第二个参数是正则表达式的字符串。
sub() 方法返回一个已应用替换的字符串:
names_regex = re.compile(r'Agent \w+')
names_regex.sub('CENSORED', 'Agent Alice gave the secret documents to Agent Bob.')
'CENSORED gave the secret documents to CENSORED.'
登录后即可答题并追踪学习进度
sub() 方法的作用是什么?管理复杂的 Regexes
为了告诉 re.compile() 函数忽略正则表达式字符串中的空格和注释,“详细模式”可以通过将变量 re.VERBOSE 作为第二个参数传递给 re.compile() 来启用。
现在,而不是像这样难以阅读的正则表达式:
phone_regex = re.compile(r'((\d{3}|\(\d{3}\))?(\s|-|\.)?\d{3}(\s|-|\.)\d{4}(\s*(ext|x|ext.)\s*\d{2,5})?)')
您可以像这样将正则表达式分布在多行上并添加注释:
phone_regex = 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)
登录后即可答题并追踪学习进度
re.compile() 的 re.VERBOSE 会做什么?