Python 正则表达式速查表和示例
以上可视化图是使用debuggex 为该模式创建的屏幕截图。 r'\bpar(en|ro)?t\b'
正则表达式(或 RE)指定一组与之匹配的字符串;本模块中的函数允许您检查特定字符串是否与给定的正则表达式匹配。
本文概述并举例说明了re内置模块(Python 3.8+)实现的正则表达式语法。除非另有说明,否则默认使用 ASCII 字符集。本文节选自我的著作《Python re(gex)?》。
定义正则表达式的元素
| 锚 | 描述 |
|---|---|
\A |
将匹配范围限制在字符串的开头。 |
\Z |
将匹配范围限制在字符串末尾。 |
^ |
将匹配范围限制在行的开头。 |
$ |
将匹配范围限制在行尾。 |
\n |
换行符用作行分隔符 |
re.MULTILINE或者re.M |
标记用于将输入视为多行字符串 |
\b |
将匹配范围限制在单词的开头/结尾。 |
| 单词字符:字母、数字、下划线 | |
\B |
\b凡是不匹配的地方都匹配 |
^上表中的 `\` 、`\ $` 和`\` 是元字符,因为这些字符具有特殊含义。给字符添加前缀可以移除其特殊含义,使其按字面意思匹配。例如,` \`将匹配一个字符,而不是作为锚点。\\\^^
| 特征 | 描述 |
|---|---|
| |
多个 RE 组合成条件 OR |
| 每个备选方案都可以有独立的锚点。 | |
(RE) |
群体模式,也称捕获群体 |
a(b|c)d与abd|acd |
|
(?:RE) |
非捕获组 |
(?P<name>pat) |
命名捕获组 |
. |
匹配除换行符以外的任何字符\n |
[] |
角色类别,与众多角色中的一个相匹配 |
| 贪婪量词 | 描述 |
|---|---|
* |
匹配零次或多次 |
+ |
一次或多次 |
? |
匹配零次或一次 |
{m,n} |
比赛m时间n(含) |
{m,} |
至少匹配 m 次 |
{,n} |
时间匹配n(包括0时间) |
{n} |
匹配 n 次 |
pat1.*pat2 |
pat1介于两者之间的任意数量的字符pat2 |
pat1.*pat2|pat2.*pat1 |
两者匹配pat1,pat2顺序不限 |
这里的“贪婪”指的是上述量词会尽可能多地匹配符合正则表达式整体规则的内容。?在贪婪量词后添加 `a` 会将其变为非贪婪量词,即尽可能少地匹配内容。量词可以应用于字面字符、字符组、反向引用和字符类。
| 角色职业 | 描述 |
|---|---|
[aeiou] |
匹配任意元音 |
[^aeiou] |
^反转选择,因此可以匹配任何辅音 |
[a-f] |
-定义了一个范围,因此它匹配 abcdef 中的任何字符。 |
\d |
匹配一个数字,与[0-9] |
\D |
匹配非数字,相同[^0-9]或[^\d] |
\w |
匹配单词字符,与[a-zA-Z0-9_] |
\W |
匹配非单词字符,与……相同[^a-zA-Z0-9_]或[^\w] |
\s |
匹配空白字符,与[\ \t\n\r\f\v] |
\S |
匹配非空白字符,与[^\ \t\n\r\f\v]或[^\s] |
| 环顾四周 | 描述 |
|---|---|
| 环顾四周 | 自定义断言,零宽度锚点 |
(?!pat) |
负面前瞻断言 |
(?<!pat) |
负面后视断言 |
(?=pat) |
积极展望断言 |
(?<=pat) |
正面回溯断言 |
(?!pat1)(?=pat2) |
可以按任意顺序指定多个断言。 |
| 它们标记匹配的位置,而无需消耗字符。 | |
((?!pat).)* |
否定一个分组,类似于否定字符类 |
| 旗帜 | 描述 |
|---|---|
re.IGNORECASE或者re.I |
忽略大小写 |
re.DOTALL或者re.S |
允许.元字符匹配换行符 |
flags=re.S|re.I |
|可以使用运算符组合多个标志。 |
re.MULTILINE或者re.M |
允许^锚点$按行匹配 |
re.VERBOSE或者re.X |
允许使用实际的空格进行对齐 |
#并在角色后添加注释 |
|
逃生空间,以及#必要时作为实际反应堆的一部分 |
|
re.ASCII或者re.A |
仅匹配 ASCII 字符,例如\b, \w, \d,\s |
| 及其相反形式,仅适用于 Unicode 模式 | |
re.LOCALE或者re.L |
使用区域设置来设置字节模式和 8 位区域设置 |
(?#comment) |
另一种添加评论的方式,而不是标记。 |
(?flags:pat) |
仅适用于此的内联标志pat,会覆盖flags参数 |
flags 用于i,re.I用于s,re.S等等,L除了re.L |
|
(?-flags:pat) |
仅对此否定标志pat |
(?flags-flags:pat) |
仅针对此应用和否定特定标志pat |
(?flags) |
为整个正则表达式应用标志,只能在正则表达式开始时使用。 |
如有锚点,应在之后指定。(?flags) |
| 匹配部分 | 描述 |
|---|---|
re.Match目的 |
匹配部分、位置等详细信息 |
m[0]或者m.group(0) |
re.Match对象中所有匹配的部分m |
m[n]或者m.group(n) |
匹配第 n 个捕获组的部分 |
m.groups() |
所有捕获组匹配部分的元组 |
m.span() |
匹配部分的起始索引和结束索引(+1)。 |
| 传递一个数字以获取特定捕获组的范围 | |
也可以使用m.start()和m.end() |
|
\N |
反向引用,给出第 N 个捕获组的匹配部分 |
| 适用于搜索和替换部分 | |
可能的值:\1,\2最多\99不超过指定位数 |
|
\g<N> |
反向引用,给出第 N 个捕获组的匹配部分 |
可能的值:\g<0>,,\g<1>等等(不限于 99) |
|
\g<0>指整个匹配部分 |
|
(?P<name>pat) |
命名捕获组 |
指'name'对象re.Match |
|
请参考(?P=name)搜索部分 |
|
请参阅\g<name>替换部分 |
|
groupdict |
对re.Match对象应用的方法 |
将命名捕获组部分作为dict |
\0及\100以后的值被视为八进制值,因此不能用作反向引用。
模块功能
| 功能 | 描述 |
|---|---|
re.search |
检查给定的模式是否出现在输入字符串中的任何位置 |
输出结果是一个re.Match对象,可用于条件表达式。 |
|
| r-strings 是定义 RE 的首选。 | |
| 使用字节模式进行字节输入 | |
| Python 还维护着一个小型缓存,用于存储最近的 RE 语句。 | |
re.fullmatch |
确保模式与整个输入字符串匹配 |
re.compile |
编译可重用的模式,输出re.Pattern对象 |
re.sub |
搜索和替换 |
re.sub(r'pat', f, s) |
f以re.Match对象为参数的函数 |
re.escape |
自动转义所有元字符 |
re.split |
基于正则表达式拆分字符串 |
| 分组匹配的文本将作为输出的一部分 | |
| 组外匹配模式的部分将不会输出 | |
re.findall |
返回所有匹配项的列表 |
| 如果只使用一个捕获组,则仅返回该捕获组的匹配项。 | |
| 对于 1+,每个元素都将是捕获组的元组 | |
| 组外匹配模式的部分将不会输出 | |
re.finditer |
re.Match包含每个匹配对象的迭代器 |
re.subn |
返回修改后的字符串及其替换次数的元组 |
函数定义如下:
re.search(pattern, string, flags=0)
re.fullmatch(pattern, string, flags=0)
re.compile(pattern, flags=0)
re.sub(pattern, repl, string, count=0, flags=0)
re.escape(pattern)
re.split(pattern, string, maxsplit=0, flags=0)
re.findall(pattern, string, flags=0)
re.finditer(pattern, string, flags=0)
re.subn(pattern, repl, string, count=0, flags=0)
正则表达式示例
最佳实践是,除非有其他格式要求,否则应始终使用原始字符串来构建正则表达式。这样可以避免正则表达式和普通带引号的字符串中反斜杠字符的特殊含义发生冲突。
- 例如
re.search
>>> sentence = 'This is a sample string'
# need to load the re module before use
>>> import re
# check if 'sentence' contains the pattern described by RE argument
>>> bool(re.search(r'is', sentence))
True
# ignore case while searching for a match
>>> bool(re.search(r'this', sentence, flags=re.I))
True
>>> bool(re.search(r'xyz', sentence))
False
# re.search output can be directly used in conditional expressions
>>> if re.search(r'ring', sentence):
... print('mission success')
...
mission success
# use raw byte strings if input is of byte data type
>>> bool(re.search(rb'is', b'This is a sample string'))
True
- 绳锚和线锚的区别
# string anchors
>>> bool(re.search(r'\Ahi', 'hi hello\ntop spot'))
True
>>> words = ['surrender', 'up', 'newer', 'do', 'ear', 'eel', 'pest']
>>> [w for w in words if re.search(r'er\Z', w)]
['surrender', 'newer']
# line anchors
>>> bool(re.search(r'^par$', 'spare\npar\ndare', flags=re.M))
True
- 例如
re.findall
# whole word par with optional s at start and optional e at end
>>> re.findall(r'\bs?pare?\b', 'par spar apparent spare part pare')
['par', 'spar', 'spare', 'pare']
# numbers >= 100 with optional leading zeros
>>> re.findall(r'\b0*[1-9]\d{2,}\b', '0501 035 154 12 26 98234')
['0501', '154', '98234']
# if multiple capturing groups are used, each element of output
# will be a tuple of strings of all the capture groups
>>> re.findall(r'([^/]+)/([^/,]+),?', '2020/04,1986/Mar')
[('2020', '04'), ('1986', 'Mar')]
# normal capture group will hinder ability to get whole match
# non-capturing group to the rescue
>>> re.findall(r'\b\w*(?:st|in)\b', 'cost akin more east run')
['cost', 'akin', 'east']
# useful for debugging purposes as well
>>> re.findall(r't.*?a', 'that is quite a fabricated tale')
['tha', 't is quite a', 'ted ta']
- 例如
re.split
# split based on one or more digit characters
>>> re.split(r'\d+', 'Sample123string42with777numbers')
['Sample', 'string', 'with', 'numbers']
# split based on digit or whitespace characters
>>> re.split(r'[\d\s]+', '**1\f2\n3star\t7 77\r**')
['**', 'star', '**']
# to include the matching delimiter strings as well in the output
>>> re.split(r'(\d+)', 'Sample123string42with777numbers')
['Sample', '123', 'string', '42', 'with', '777', 'numbers']
# use non-capturing group if capturing is not needed
>>> re.split(r'hand(?:y|ful)', '123handed42handy777handful500')
['123handed42', '777', '500']
- 搜索模式中的反向引用
# whole words that have at least one consecutive repeated character
>>> words = ['effort', 'flee', 'facade', 'oddball', 'rat', 'tool']
>>> [w for w in words if re.search(r'\b\w*(\w)\1\w*\b', w)]
['effort', 'flee', 'oddball', 'tool']
- 使用匹配部分
>>> re.search(r'b.*d', 'abc ac adc abbbc')
<re.Match object; span=(1, 9), match='bc ac ad'>
# retrieving entire matched portion, note the use of [0]
>>> re.search(r'b.*d', 'abc ac adc abbbc')[0]
'bc ac ad'
# capture group example
>>> m = re.search(r'a(.*)d(.*a)', 'abc ac adc abbbc')
# to get matched portion of second capture group
>>> m[2]
'c a'
# to get a tuple of all the capture groups
>>> m.groups()
('bc ac a', 'c a')
- 例如
re.finditer
# numbers < 350
>>> m_iter = re.finditer(r'[0-9]+', '45 349 651 593 4 204')
>>> [m[0] for m in m_iter if int(m[0]) < 350]
['45', '349', '4', '204']
# start and end+1 index of each matching portion
>>> m_iter = re.finditer(r'ab+c', 'abc ac adc abbbc')
>>> for m in m_iter:
... print(m.span())
...
(0, 3)
(11, 16)
- 例如
re.sub
>>> ip_lines = "catapults\nconcatenate\ncat"
>>> print(re.sub(r'^', r'* ', ip_lines, flags=re.M))
* catapults
* concatenate
* cat
# replace 'par' only at start of word
>>> re.sub(r'\bpar', r'X', 'par spar apparent spare part')
'X spar apparent spare Xt'
# same as: r'part|parrot|parent'
>>> re.sub(r'par(en|ro)?t', r'X', 'par part parrot parent')
'par X X X'
# remove first two columns where : is delimiter
>>> re.sub(r'\A([^:]+:){2}', r'', 'foo:123:bar:baz', count=1)
'bar:baz'
- 替换部分中的反向引用
# remove consecutive duplicate words separated by space
>>> re.sub(r'\b(\w+)( \1)+\b', r'\1', 'aa a a a 42 f_1 f_1 f_13.14')
'aa a 42 f_1 f_13.14'
# add something around the matched strings
>>> re.sub(r'\d+', r'(\g<0>0)', '52 apples and 31 mangoes')
'(520) apples and (310) mangoes'
# swap words that are separated by a comma
>>> re.sub(r'(\w+),(\w+)', r'\2,\1', 'good,bad 42,24')
'bad,good 24,42'
- 在替换部分使用函数
re.sub
>>> from math import factorial
>>> numbers = '1 2 3 4 5'
>>> def fact_num(n):
... return str(factorial(int(n[0])))
...
>>> re.sub(r'\d+', fact_num, numbers)
'1 2 6 24 120'
# using lambda
>>> re.sub(r'\d+', lambda m: str(factorial(int(m[0]))), numbers)
'1 2 6 24 120'
- 环视示例
# change 'foo' only if it is not followed by a digit character
# note that end of string satisfies the given assertion
# foofoo has 2 matches as the assertion doesn't consume characters
>>> re.sub(r'foo(?!\d)', r'baz', 'hey food! foo42 foot5 foofoo')
'hey bazd! foo42 bazt5 bazbaz'
# change whole word only if it is not preceded by : or -
>>> re.sub(r'(?<![:-])\b\w+\b', r'X', ':cart <apple -rest ;tea')
':cart <X -rest ;X'
# match digits only if it is preceded by - and followed by ; or :
>>> re.findall(r'(?<=-)\d+(?=[:;])', 'fo-5, ba3; x-83, y-20: f12')
['20']
# words containing 'b' and 'e' and 't' in any order
>>> words = ['sequoia', 'questionable', 'exhibit', 'equation']
>>> [w for w in words if re.search(r'(?=.*b)(?=.*e).*t', w)]
['questionable', 'exhibit']
# match if 'do' is not there between 'at' and 'par'
>>> bool(re.search(r'at((?!do).)*par', 'fox,cat,dog,parrot'))
False
# match if 'go' is not there between 'at' and 'par'
>>> bool(re.search(r'at((?!go).)*par', 'fox,cat,dog,parrot'))
True
- 例如
re.compile
正则表达式可以使用函数进行编译re.compile,该函数会返回一个re.Pattern对象。所有顶层re模块函数都可以作为该对象的方法使用。如果正则表达式需要在多个地方使用,或者在循环中多次调用,编译它就能提高速度。默认情况下,Python 会维护一个最近使用的正则表达式列表,因此对于简单的用例,速度优势并不明显。
>>> pet = re.compile(r'dog')
>>> type(pet)
<class 're.Pattern'>
>>> bool(pet.search('They bought a dog'))
True
>>> bool(pet.search('A cat crossed their path'))
False
>>> pat = re.compile(r'\([^)]*\)')
>>> pat.sub('', 'a+b(addition) - foo() + c%d(#modulo)')
'a+b - foo + c%d'
>>> pat.sub('', 'Hi there(greeting). Nice day(a(b)')
'Hi there. Nice day'
Python re(gex)? 书籍
访问我的代码仓库Python re(gex)?,了解我撰写的关于 Python 正则表达式的书籍详情。这本电子书使用了大量示例,从最基础的概念开始讲解,并逐步引入更高级的概念。书中还涵盖了第三方模块 regex。本文中的速查表和示例均基于本书内容。
使用此 Leanpub 链接可享受折扣价。
文章来源:https://dev.to/learnbyexample/python-regular-expression-cheatsheet-and-examples-20bn
