>>> import re
>>> m = re.search('(?<=abc)def', 'abcdef')
>>> m.group(0)
'def'
这个例子搜索一个跟随在连字符后的单词:
>>> m = re.search(r'(?<=-)\w+', 'spam-egg')
>>> m.group(0)
'egg'
在 3.5 版本发生变更: 添加定长组合引用的支持。
(?(id/name)yes-pattern|no-pattern)如果给定的 id 或 name 存在,将会尝试匹配 yes-pattern ,否则就尝试匹配 no-pattern,no-pattern 可选,也可以被忽略。比如, (<)?(\w+@\w+(?:\.\w+)+)(?(1)>|$) 是一个email样式匹配,将匹配 '<[email protected]>' 或 '[email protected]' ,但不会匹配 '<[email protected]' ,也不会匹配 '[email protected]>'。
在 3.12 版本发生变更: 分组 id 只能包含 ASCII 数码。 在 bytes 模式中,分组 name 只能包含 ASCII 范围内的字节值 (b'\x00'-b'\x7f')。
由 '\' 和一个字符组成的特殊序列在以下列出。 如果普通字符不是ASCII数位或者ASCII字母,那么正则样式将匹配第二个字符。比如,\$ 匹配字符 '$'.
\number匹配数字代表的组合。每个括号是一个组合,组合从1开始编号。比如 (.+) \1 匹配 'the the' 或者 '55 55', 但不会匹配 'thethe' (注意组合后面的空格)。这个特殊序列只能用于匹配前面99个组合。如果 number 的第一个数位是0, 或者 number 是三个八进制数,它将不会被看作是一个组合,而是八进制的数字值。在 '[' 和 ']' 字符集合内,任何数字转义都被看作是字符。
\A只匹配字符串开始。
\b匹配空字符串,但只在单词开始或结尾的位置。 一个单词被定义为一个单词字符的序列。 注意在通常情况下,\b 被定义为 \w 和 \W 字符之间的边界(反之亦然),或是 \w 和字符串开始或结尾之间的边界。 这意味着 r'\bat\b' 将匹配 'at', 'at.', '(at)' 和 'as at ay' 但不匹配 'attempt' 或 'atlas'。
Unicode (str) 模式中默认的单词类字符是 Unicode 字母数字和下划线,但这可以通过使用 ASCII 旗标来改变。 如果使用了 LOCALE 旗标则单词边界将根据当前语言区域来确定。
在一个字符范围内,\b 代表退格符,以便与 Python 的字符串字面值保持兼容。
\B匹配空字符串,但仅限于它 不在 单词的开头或结尾的情况。 这意味着 r'at\B' 将匹配 'athens', 'atom', 'attorney',但不匹配 'at', 'at.' 或 'at!'。 \B 与 \b 正相反,这样 Unicode (str) 模式中的单词类字符是 Unicode 字母数字或下划线,但这可以通过使用 ASCII 旗标来改变。 如果使用了 LOCALE 旗标则单词边界将根据当前语言区域来确定。
请注意 \B 不会匹配空字符串,这与其他编程语言如 Perl 的 RE 实现不同。 此行为是出于兼容性考虑而保留的。
对于 Unicode (str) 样式:匹配任意 Unicode 十进制数码(也就是说,任何属于 Unicode 字符类别 [Nd] 的字符)。 这包括 [0-9],还包括许多其他的数码类字符。
如果使用了 ASCII 旗标则匹配 [0-9]
对于8位(bytes)样式:匹配 ASCII 字符集内的任意十进制数码;这等价于 [0-9]。
对于 Unicode (str) 样式:匹配 Unicode 空白字符(如 str.isspace() 所定义的)。 这包括 [ \t\n\r\f\v],还包括许多其他字符,例如许多语言中由排版规则约定的非中断空白字符。
如果使用了 ASCII 旗标则匹配 [ \t\n\r\f\v]。
对于8位(bytes)样式:匹配ASCII中的空白字符,就是 [ \t\n\r\f\v] 。
对于 Unicode (str) 样式:匹配 Unicode 单词类字符;这包括所有 Unicode 字母数字类字符 (由 str.isalnum() 定义),以及下划线 (_)。
如果使用了 ASCII 旗标则匹配 [a-zA-Z0-9_]。
对于8位(bytes)样式:匹配在 ASCII 字符集中被视为字母数字的字符;这等价于 [a-zA-Z0-9_]。 如果使用了 LOCALE 旗标,则匹配在当前语言区域中被视为字母数字的字符以及下划线。
\W匹配不属于单词类字符的任意字符。 这与 \w 正相反。 在默认情况下,将匹配除下划线 (_) 以外的 str.isalnum() 返回 False 的字符。
如果使用了 ASCII 旗标则匹配 [^a-zA-Z0-9_]。
如果使用了 LOCALE 旗标,则匹配在当前语言区域中不属于字母数字且不为下划线的字符。
\Z只匹配字符串尾。
Python 字符串字面值支持的大多数 转义序列 也被正则表达式解析器所接受:
\a \b \f \n
\N \r \t \u
\U \v \x \\
(注意 \b 被用于表示词语的边界,它只在字符集合内表示退格,比如 [\b] 。)
'\u', '\U' 和 '\N' 转义序列仅在 Unicode (str) 模式中可被识别。 在字节串模式中它们会导致错误。 未知的 ASCII 字母转义符被保留在未来使用并会被视为错误。
八进制转义包含为一个有限形式。如果首位数字是 0, 或者有三个八进制数位,那么就认为它是八进制转义。其他的情况,就看作是组引用。对于字符串文本,八进制转义最多有三个数位长。
在 3.3 版本发生变更: 增加了 '\u'
和 '\U' 转义序列。
在 3.6 版本发生变更: 由 '\' 和一个ASCII字符组成的未知转义会被看成错误。
在 3.8 版本发生变更: 增加了 '\N{name}' 转义序列。 与在字符串字面值中一样,它扩展了指定的 Unicode 字符 (例如 '\N{EM DASH}')。
模块内容
模块定义了几个函数、常量,和一个异常。有些函数是编译后的正则表达式方法的简化版本(少了一些特性)。重要的应用程序大多会在使用前先编译正则表达式。
在 3.6 版本发生变更: 标志常量现在是 RegexFlag 类的实例,这个类是 enum.IntFlag 的子类。
class re.RegexFlag
包含以下列出的正则表达式选项的 enum.IntFlag 类。
Added in version 3.11: - added to __all__
re.ASCII
使 \w, \W, \b, \B, \d, \D, \s 和 \S 执行仅限 ASCII 匹配而不是完整的 Unicode 匹配。 这仅对 Unicode (str) 模式有意义,而对字节串模式将被忽略。
对应于内联旗标 (?a)。
U 旗标仍然存在以保持下向兼容性,但在 Python 3 中是多余的因为对于 str 模式默认使用 Unicode,并且 Unicode 匹配对于 bytes 模式则是不允许的。 UNICODE 和内联旗标 (?u) 同样也是多余的。
re.IGNORECASE
执行忽略大小写的匹配;[A-Z] 这样的表达式也将匹配小写字母。 完全的 Unicode 匹配 (如 Ü 将匹配 ü) 同样适用,除非使用了 ASCII 旗标来禁用非 ASCII 匹配。 当前语言区域不会改变该旗标的效果,除非还使用了 LOCALE 旗标。
对应于内联旗标 (?i)。
请注意当 Unicode 模式 [a-z] 或 [A-Z] 与 IGNORECASE 旗标一起使用时,它们将匹配 52 个 ASCII 字母和 4 个额外的非 ASCII 字母: 'İ' (U+0130, 大写拉丁字母 I 带有上方的点), 'ı' (U+0131, 小写拉丁字母 i 不带上方的点), 'ſ' (U+017F, 小写拉丁字母长 s) 和 'K' (U+212A, 开尔文标记)。 如果使用了 ASCII 旗标,则只匹配字母 'a' 到 'z' 和 'A' 到 'Z'。
该旗标已不建议使用;请考虑改用 Unicode 匹配。 语言区域机制相当不可靠因为它每次只能处理一种“文化”并且只适用于 8 位语言区域。 Unicode (str) 模式默认启用 Unicode 匹配并且能够处理不同的语言区域和语言。
在 3.6 版本发生变更: LOCALE 仅适用于 bytes 模式并且不能兼容 ASCII。
在 3.7 版本发生变更: 设置了 LOCALE 旗标的已编译正则表达式对象不会再依赖于编译时的语言区域。 只有在匹配时的语言区域才会影响匹配结果。
re.MULTILINE
在指定之后,模式字符 '^' 将匹配字符串的开始和每一行的开头(紧随在换行符之后);而模式字符 '$' 将匹配字符串的末尾和每一行的末尾(紧接在换行符之前)。 在默认情况下,'^' 只匹配字符串的开头,而``'$'`` 只匹配字符串的末尾和紧接在字符串末尾(可能存在的)换行符之前。
对应于内联旗标 (?m)。
re.NOFLAG
表示未应用任何旗标,该值为 0。 该旗标可被用作某个函数关键字参数的默认值或者用作将与其他旗标进行有条件 OR 运算的基准值。 用作默认值的例子:
def myfunc(text, flag=re.NOFLAG):
return re.match(text, flag)
Added in version 3.11.
re.VERBOSE
这个旗标允许你通过在视觉上分隔表达式的逻辑段落和添加注释来编写更为友好并更具可读性的正则表达式。 表达式中的空白符会被忽略,除非是在字符类中,或前面有一个未转义的反斜杠,或者是在 *?, (?: 或 (?P<...> 等形符之内。 例如,(? : 和 * ? 是不被允许的。 当一个行内包含不在字符类中并且前面没有未转义反斜杠的 # 时,则从最左边的此 # 直至行尾的所有字符都会被忽略。
意思就是下面两个正则表达式等价地匹配一个十进制数字:
a = re.compile(r"""\d + # 整数部分
\. # 小数点
\d * # 几个小数位""", re.X)
b = re.compile(r"\d+\.\d*")
对应内联标记 (?x) 。
re.compile(pattern, flags=0)
将正则表达式的样式编译为一个 正则表达式对象 (正则对象),可以用于匹配,通过这个对象的方法 match(), search() 以及其他如下描述。
表达式的行为可通过指定 flags 值来修改。 值可以是任意 flags 变量,可使用按位 OR (| 运算符) 进行组合。
prog = re.compile(pattern)
result = prog.match(string)
result = re.match(pattern, string)
如果需要多次使用这个正则表达式的话,使用 re.compile() 和保存这个正则对象以便复用,可以让程序更加高效。
通过 re.compile() 编译后的样式,和模块级的函数会被缓存, 所以少数的正则表达式使用无需考虑编译的问题。
re.search(pattern, string, flags=0)
扫描整个 string 查找正则表达式 pattern 产生匹配的第一个位置,并返回相应的 Match。 如果字符串中没有与模式匹配的位置则返回 None;请注意这不同于在字符串的某个位置上找到零长度匹配。
表达式的行为可通过指定 flags 值来修改。 值可以是任意 flags 变量,可使用按位 OR (| 运算符) 进行组合。
re.match(pattern, string, flags=0)
如果 string 开头的零个或多个字符与正则表达式 pattern 匹配,则返回相应的 Match。 如果字符串与模式不匹配则返回 None;请注意这与零长度匹配是不同的。
注意即便是 MULTILINE 多行模式, re.match() 也只匹配字符串的开始位置,而不匹配每行开始。
如果你想定位 string 的任何位置,使用 search() 来替代(也可参考 search() vs. match() )
表达式的行为可通过指定 flags 值来修改。 值可以是任意 flags 变量,可使用按位 OR (| 运算符) 进行组合。
re.fullmatch(pattern, string, flags=0)
如果整个 string 与正则表达式 pattern 匹配,则返回相应的 Match。 如果字符串与模式不匹配则返回 None;请注意这与零长度匹配是不同的。
表达式的行为可通过指定 flags 值来修改。 值可以是任意 flags 变量,可使用按位 OR (| 运算符) 进行组合。
Added in version 3.4.
re.split(pattern, string, maxsplit=0, flags=0)
用 pattern 分开 string 。 如果在 pattern 中捕获到括号,那么所有的组里的文字也会包含在列表里。如果 maxsplit 非零, 最多进行 maxsplit 次分隔, 剩下的字符全部返回到列表的最后一个元素。
>>> re.split(r'\W+', 'Words, words, words.'
)
['Words', 'words', 'words', '']
>>> re.split(r'(\W+)', 'Words, words, words.')
['Words', ', ', 'words', ', ', 'words', '.', '']
>>> re.split(r'\W+', 'Words, words, words.', 1)
['Words', 'words, words.']
>>> re.split('[a-f]+', '0a3B9', flags=re.IGNORECASE)
['0', '3', '9']
如果分隔符里有捕获组合,并且匹配到字符串的开始,那么结果将会以一个空字符串开始。对于结尾也是一样
>>> re.split(r'(\W+)', '...words, words...')
['', '...', 'words', ', ', 'words', '...', '']
这样的话,分隔组将会出现在结果列表中同样的位置。
样式的空匹配仅在与前一个空匹配不相邻时才会拆分字符串。
>>> re.split(r'\b', 'Words, words, words.')
['', 'Words', ', ', 'words', ', ', 'words', '.']
>>> re.split(r'\W*', '...words...')
['', '', 'w', 'o', 'r', 'd', 's', '', '']
>>> re.split(r'(\W*)', '...words...')
['', '...', '', '', 'w', '', 'o', '', 'r', '', 'd', '', 's', '...', '', '', '']
表达式的行为可通过指定 flags 值来修改。 值可以是任意 flags 变量,可使用按位 OR (| 运算符) 进行组合。
在 3.1 版本发生变更: 增加了可选标记参数。
在 3.7 版本发生变更: 增加了空字符串的样式分隔。
re.findall(pattern, string, flags=0)
返回 pattern 在 string 中的所有非重叠匹配,以字符串列表或字符串元组列表的形式。对 string 的扫描从左至右,匹配结果按照找到的顺序返回。 空匹配也包括在结果中。
返回结果取决于模式中捕获组的数量。如果没有组,返回与整个模式匹配的字符串列表。如果有且仅有一个组,返回与该组匹配的字符串列表。如果有多个组,返回与这些组匹配的字符串元组列表。非捕获组不影响结果。
>>> re.findall(r'\bf[a-z]*', 'which foot or hand fell fastest')
['foot', 'fell', 'fastest']
>>> re.findall(r'(\w+)=(\d+)', 'set width=20 and height=10')
[('width', '20'), ('height', '10')]
表达式的行为可通过指定 flags 值来修改。 值可以是任意 flags 变量,可使用按位 OR (| 运算符) 进行组合。
在 3.7 版本发生变更: 非空匹配现在可以在前一个空匹配之后出现了。
re.finditer(pattern, string, flags=0)
针对正则表达式 pattern 在 string 里的所有非重叠匹配返回一个产生 Match 对象的 iterator。 string 将被从左至右地扫描,并且匹配也将按被找到的顺序返回。 空匹配也会被包括在结果中。
表达式的行为可通过指定 flags 值来修改。 值可以是任意 flags 变量,可使用按位 OR (| 运算符) 进行组合。
在 3.7 版本发生变更: 非空匹配现在可以在前一个空匹配之后出现了。
re.sub(pattern, repl, string, count=0, flags=0)
返回通过使用 repl 替换在 string 最左边非重叠出现的 pattern 而获得的字符串。 如果样式没有找到,则不加改变地返回 string。 repl 可以是字符串或函数;如为字符串,则其中任何反斜杠转义序列都会被处理。 也就是说,\n 会被转换为一个换行符,\r 会被转换为一个回车符,依此类推。 未知的 ASCII 字符转义序列保留在未来使用,会被当作错误来处理。 其他未知转义序列例如 \& 会保持原样。 向后引用像是 \6 会用样式中第 6 组所匹配到的子字符串来替换。 例如:
>>> re.sub(r'def\s+([a-zA-Z_][a-zA-Z_0-9]*)\s*\(\s*\):',
... r'static PyObject*\npy_\1(void)\n{',
... 'def myfunc():')
'static PyObject*\npy_myfunc(void)\n{'
如果 repl 是一个函数,则它会针对每次 pattern 的非重叠出现的情况被调用。 该函数接受单个 Match 参数,并返回替换字符串。 例如:
>>> def dashrepl(matchobj):
... if matchobj.group(0) == '-': return ' '
... else: return '-'
>>> re.sub('-{1,2}', dashrepl, 'pro----gram-files')
'pro--gram files'
>>> re.sub(r'\sAND\s', ' & ', 'Baked Beans And Spam', flags=re.IGNORECASE)
'Baked Beans & Spam'
模式可以是一个字符串或者 Pattern。
可选参数 count 是要替换的最大次数;count 必须是非负整数。如果省略这个参数或设为 0,所有的匹配都会被替换。 样式的空匹配仅在与前一个空匹配不相邻时才会被替换,所以 sub('x*', '-', 'abxd') 返回 '-a-b--d-' 。
在字符串类型的 repl 参数里,如上所述的转义和向后引用中,\g<name> 会使用命名组合 name,(在 (?P<name>…) 语法中定义) \g<number> 会使用数字组;\g<2> 就是 \2,但它避免了二义性,如 \g<2>0。 \20 就会被解释为组20,而不是组2后面跟随一个字符 '0'。向后引用 \g<0> 把 pattern 作为一整个组进行引用。
表达式的行为可通过指定 flags 值来修改。 值可以是任意 flags 变量,可使用按位 OR (| 运算符) 进行组合。
在 3.1 版本发生变更: 增加了可选标记参数。
在 3.5 版本发生变更: 不匹配的组合替换为空字符串。
在 3.6 版本发生变更: pattern 中的未知转义(由 '\' 和一个 ASCII 字符组成)被视为错误。
在 3.7 版本发生变更: repl 中的未知转义(由 '\' 和一个 ASCII 字符组成)被视为错误。
在 3.7 版本发生变更: 样式中的空匹配相邻接时会被替换。
在 3.12 版本发生变更: 分组 id 只能包含 ASCII 数码。 在 bytes 替换字符串中,分组 name 只能包含 ASCII 范围内的字节值 (b'\x00'-b'\x7f')。
re.subn(pattern, repl, string, count=0, flags=0)
行为与 sub() 相同,但是返回一个元组 (字符串, 替换次数).
在 3.1 版本发生变更: 增加了可选标记参数。
在 3.5 版本发生变更: 不匹配的组合替换为空字符串。
表达式的行为可通过指定 flags 值来修改。 值可以是任意 flags 变量,可使用按位 OR (| 运算符) 进行组合。
re.escape(pattern)
转义 pattern 中的特殊字符。如果你想对任意可能包含正则表达式元字符的文本字符串进行匹配,它就是有用的。比如
>>> print(re.escape('https://www.python.org'))
https://www\.python\.org
>>> legal_chars = string.ascii_lowercase + string.digits + "!#$%&'*+-.^_`|~:"
>>> print('[%s]+' % re.escape(legal_chars))
[abcdefghijklmnopqrstuvwxyz0123456789!\#\$%\&'\*\+\-\.\^_`\|\~:]+
>>> operators = ['+', '-', '*', '/', '**']
>>> print('|'.join(map(re.escape, sorted(operators, reverse=True))))
/|\-|\+|\*\*|\*
这个函数不能被用于 sub() 和 subn() 的替换字符串,只有反斜杠应该被转义。 例如:
>>> digits_re
= r'\d+'
>>> sample = '/usr/sbin/sendmail - 0 errors, 12 warnings'
>>> print(re.sub(digits_re, digits_re.replace('\\', r'\\'), sample))
/usr/sbin/sendmail - \d+ errors, \d+ warnings
在 3.3 版本发生变更: '_' 不再被转义。
在 3.7 版本发生变更: 只有在正则表达式中具有特殊含义的字符才会被转义。 因此, '!', '"', '%', "'", ',', '/', ':', ';', '<', '=', '>', '@' 和 "`" 将不再会被转义。
exception re.error(msg, pattern=None, pos=None)
当传递给函数的正则表达式不合法(比如括号不匹配),或者在编译或匹配过程中出现其他错误时,会引发异常。所给字符串不匹配所给模式不会引发异常。异常实例有以下附加属性:
msg
未格式化的错误消息。
Pattern.search(string[, pos[, endpos]])
扫描整个 string 查找该正则表达式产生匹配的第一个位置,并返回相应的 Match。 如果字符串中没有与模式匹配的位置则返回 None;请注意这不同于在字符串的某个位置上找到零长度匹配。
可选的第二个参数 pos 给出了字符串中开始搜索的位置索引;默认为 0,它不完全等价于字符串切片; '^' 样式字符匹配字符串真正的开头,和换行符后面的第一个字符,但不会匹配索引规定开始的位置。
可选参数 endpos 限定了字符串搜索的结束;它假定字符串长度到 endpos , 所以只有从 pos 到 endpos - 1 的字符会被匹配。如果 endpos 小于 pos,就不会有匹配产生;另外,如果 rx 是一个编译后的正则对象, rx.search(string, 0, 50) 等价于 rx.search(string[:50], 0)。
>>> pattern = re.compile("d")
>>> pattern.search("dog") # 在索引0处匹配
<re.Match object; span=(0, 1), match='d'>
>>> pattern.search("dog", 1) # 没有匹配项;搜索不包括“D”
Pattern.match(string[, pos[, endpos]])
如果字符串 开头 的零个或多个字符与此正则表达式匹配,则返回相应的 Match。 如果字符串与模式不匹配则返回 None;请注意这与零长度匹配是不同的。
可选参数 pos 和 endpos 与 search() 含义相同。
>>> pattern = re.compile("o")
>>> pattern.match("dog") # 不匹配因为 "o" 不在 "dog" 的开头。
>>> pattern.match("dog", 1) # 匹配因为 "o" 是 "dog" 的第 2 个字符。
<re.Match object; span=(1, 2), match='o'>
如果你想定位匹配在 string 中的位置,使用 search() 来替代(另参考 search() vs. match())。
Pattern.fullmatch(string[, pos[, endpos]])
如果整个 string 与此正则表达式匹配,则返回相应的 Match。 如果字符串与模式不匹配则返回 None;请注意这与零长度匹配是不同的。
可选参数 pos 和 endpos 与 search() 含义相同。
>>> pattern = re.compile("o[gh]")
>>> pattern.fullmatch("dog") # 不匹配因为 "o" 不在 "dog" 的开头。
>>> pattern.fullmatch("ogre") # 不匹配因为不是整个字符串都匹配。
>>> pattern.fullmatch("doggie", 1, 3) # 在给定的限制下匹配。
<re.Match object; span=(1, 3), match='og'>
Added in version 3.4.
匹配对象
匹配对象总是有一个布尔值 True。如果没有匹配的话 match() 和 search() 返回 None 所以你可以简单的用 if 语句来判断是否匹配
match = re.search(pattern, string)
if match:
process(match)
Match.expand(template)
返回通过在模板字符串 template 上执行反斜杠替换所获得的字符串,就像 sub() 方法所做的那样。 转义符例如 \n 将被转换为适当的字符,而数字反向引用 (\1, \2) 和命名反向引用 (\g<1>, \g<name>) 将被替换为相应分组的内容。 反向引用 \g<0> 将被替换为整个匹配的内容。
在 3.5 版本发生变更: 不匹配的组合替换为空字符串。
Match.group([group1, ...])
返回一个或者多个匹配的子组。如果只有一个参数,结果就是一个字符串,如果有多个参数,结果就是一个元组(每个参数对应一个项),如果没有参数,组1默认到0(整个匹配都被返回)。 如果一个组N 参数值为 0,相应的返回值就是整个匹配字符串;如果它是一个范围 [1..99],结果就是相应的括号组字符串。如果一个组号是负数,或者大于样式中定义的组数,就引发一个 IndexError 异常。如果一个组包含在样式的一部分,并被匹配多次,就返回最后一个匹配。:
>>> m = re.match(r"(\w+) (\w+)", "Isaac Newton, physicist")
>>> m.group(0) # 整个匹配
'Isaac Newton'
>>> m.group(1) # 第一个圆括号标记的子分组。
'Isaac'
>>> m.group(2) # 第二个圆括号标记的子分组。
'Newton'
>>> m.group(1, 2) # 使用多个参数则返回一个元组。
('Isaac', 'Newton')
如果正则表达式使用了 (?P<name>...) 语法, groupN 参数就也可能是命名组合的名字。如果一个字符串参数在样式中未定义为组合名,就引发一个 IndexError 异常。
一个相对复杂的例子
>>> m = re.match(r"(?P<first_name>\w+) (?P<last_name>\w+)", "Malcolm Reynolds")
>>> m.group('first_name')
'Malcolm'
>>> m.group('last_name')
'Reynolds'
命名组合同样可以通过索引值引用
>>> m.group(1)
'Malcolm'
>>> m.group(2)
'Reynolds'
如果一个组匹配成功多次,就只返回最后一个匹配
>>> m = re
.match(r"(..)+", "a1b2c3") # 匹配 3 次。
>>> m.group(1) # 只返回最后一个匹配。
Match.__getitem__(g)
这个等价于 m.group(g)。这允许更方便的引用一个匹配
>>> m = re.match(r"(\w+) (\w+)", "Isaac Newton, physicist")
>>> m[0] # 整个匹配
'Isaac Newton'
>>> m[1] # 第一个圆括号标记的子分组。
'Isaac'
>>> m[2] # 第二个圆括号标记的子分组。
'Newton'
命名分组也是受支持的:
>>> m = re.match(r"(?P<first_name>\w+) (?P<last_name>\w+)", "Isaac Newton")
>>> m['first_name']
'Isaac'
>>> m['last_name']
'Newton'
Added in version 3.6.
Match.groups(default=None)
返回一个元组,包含所有匹配的子组,在样式中出现的从1到任意多的组合。 default 参数用于不参与匹配的情况,默认为 None。
>>> m = re.match(r"(\d+)\.(\d+)", "24.1632")
>>> m.groups()
('24', '1632')
如果我们使小数点可选,那么不是所有的组都会参与到匹配当中。这些组合默认会返回一个 None ,除非指定了 default 参数。
>>> m = re.match(r"(\d+)\.?(\d+)?", "24")
>>> m.groups() # 第二个分组默认为 None。
('24', None)
>>> m.groups('0') # 现在,第二个分组默认为 '0'。
('24', '0')
Match.groupdict(default=None)
返回一个字典,包含了所有的 命名 子组。key就是组名。 default 参数用于不参与匹配的组合;默认为 None。 例如
>>> m = re.match(r"(?P<first_name>\w+) (?P<last_name>\w+)", "Malcolm Reynolds")
>>> m.groupdict()
{'first_name': 'Malcolm', 'last_name': 'Reynolds'}
Match.end([group])
返回 group 匹配到的字串的开始和结束标号。group 默认为0(意思是整个匹配的子串)。如果 group 存在,但未产生匹配,就返回 -1 。对于一个匹配对象 m, 和一个未参与匹配的组 g ,组 g (等价于 m.group(g))产生的匹配是
m.string[m.start(g):m.end(g)]
注意 m.start(group) 将会等于 m.end(group) ,如果 group 匹配一个空字符串的话。比如,在 m = re.search('b(c?)', 'cba') 之后,m.start(0) 为 1, m.end(0) 为 2, m.start(1) 和 m.end(1) 都是 2, m.start(2) 引发一个 IndexError 异常。
这个例子会从email地址中移除掉 remove_this
>>> email = "tony@tiremove_thisger.net"
>>> m = re.search("remove_this", email)
>>> email[:m.start()] + email[m.end():]
'[email protected]'
if match is None:
return None
return '<Match: %r, groups=%r>' % (match.group(), match.groups())
假设你在写一个扑克程序,一个玩家的一手牌为五个字符的串,每个字符表示一张牌,"a" 就是 A, "k" K, "q" Q, "j" J, "t" 为 10, "2" 到 "9" 表示2 到 9。
要看给定的字符串是否有效,我们可以按照以下步骤
>>> valid = re.compile(r"^[a2-9tjqk]{5}$")
>>> displaymatch(valid.match("akt5q")) # 有效。
"<Match: 'akt5q', groups=()>"
>>> displaymatch(valid.match("akt5e")) # 无效。
>>> displaymatch(valid.match("akt")) # 无效。
>>> displaymatch(valid.match("727ak")) # 有效。
"<Match: '727ak', groups=()>"
最后一手牌,"727ak" ,包含了一个对子,或者两张同样数值的牌。要用正则表达式匹配它,应该使用向后引用如下
>>> pair = re.compile(r".*(.).*\1")
>>> displaymatch(pair.match("717ak")) # 成对的 7。
"<Match: '717', groups=('7',)>"
>>> displaymatch(pair.match("718ak")) # 没有成对。
>>> displaymatch(pair.match("354aa")) # 成对的 a。
"<Match: '354aa', groups=('a',)>"
要找出对子由什么牌组成,开发者可以按照下面的方式来使用匹配对象的 group() 方法:
>>> pair = re.compile(r".*(.).*\1")
>>> pair.match("717ak").group(1)
# 会报错因为 re.match() 返回 None,它没有 group() 方法。
>>> pair.match("718ak").group(1)
Traceback (most recent call last):
File "<pyshell#23>", line 1, in <module>
re.match(r".*(.).*\1", "718ak").group(1)
AttributeError: 'NoneType' object has no attribute 'group'
>>> pair.match("354aa").group(1)
模拟 scanf()
目前 Python 没有 scanf() 的等价物。 正则表达式通常比 scanf() 格式字符串更强大,但也更冗长。 下表提供了 scanf() 格式符和正则表达式之间一些大致等价的映射。
scanf() 形符
正则表达式
[-+]?\d+
%e, %E, %f, %g
[-+]?(\d+(\.\d*)?|\.\d+)([eE][-+]?\d+)?
[-+]?(0[xX][\dA-Fa-f]+|0[0-7]*|\d+)
[-+]?[0-7]+
%x, %X
[-+]?(0[xX])?[\dA-Fa-f]+
从文件名和数字提取字符串
/usr/sbin/sendmail - 0 errors, 4 warnings
你应当这样使用 scanf()
格式
%s - %d errors, %d warnings
等价的正则表达式是:
(\S+) - (\d+) errors, (\d+) warnings
re.match() 只在字符串的开头位置检测匹配。
re.search() 在字符串中的任何位置检测匹配(这也是 Perl 在默认情况下所做的)
re.fullmatch() 检测整个字符串是否匹配
>>> re.match("c", "abcdef") # 不匹配
>>> re.search("c", "abcdef") # 匹配
<re.Match object; span=(2, 3), match='c'>
>>> re.fullmatch("p.*n", "python") # 匹配
<re.Match object; span=(0, 6), match='python'>
>>> re.fullmatch("r.*n", "python") # 不匹配
在 search() 中,可以用 '^' 作为开始来限制匹配到字符串的首位
>>> re.match("c", "abcdef") # 不匹配
>>> re.search("^c", "abcdef") # 不匹配
>>> re.search("^a", "abcdef") # 匹配
<re.Match object; span=(0, 1), match='a'>
注意 MULTILINE 多行模式中函数 match() 只匹配字符串的开始,但使用 search() 和以 '^' 开始的正则表达式会匹配每行的开始
>>> re.match("X", "A\nB\nX", re.MULTILINE) # 不匹配
>>> re.search("^X", "A\nB\nX", re.MULTILINE) # 匹配
<re.Match object; span=(4, 5), match='X'>
制作一个电话本
split() 将字符串用参数传递的样式分隔开。这个方法对于转换文本数据到易读而且容易修改的数据结构,是很有用的,如下面的例子证明。
首先,这里是输入。 它通常来自一个文件,这里我们使用三重引号字符串语法
>>> text = """Ross McFluff: 834.345.1254 155 Elm Street
... Ronald Heathmore: 892.345.3428 436 Finley Avenue
... Frank Burger: 925.541.7625 662 South Dogwood Way
... Heather Albrecht: 548.326.4584 919 Park Place"""
条目用一个或者多个换行符分开。现在我们将字符串转换为一个列表,每个非空行都有一个条目:
>>> entries = re.split("\n+", text)
>>> entries
['Ross McFluff: 834.345.1254 155 Elm Street',
'Ronald Heathmore: 892.345.3428 436 Finley Avenue',
'Frank Burger: 925.541.7625 662 South Dogwood Way',
'Heather Albrecht: 548.326.4584 919 Park Place']
最终,将每个条目分割为一个由名字、姓氏、电话号码和地址组成的列表。我们为 split() 使用了 maxsplit 形参,因为地址中包含有被我们作为分割模式的空格符:
>>> [re.split(":? ", entry, 3) for entry in entries]
[['Ross', 'McFluff', '834.345.1254', '155 Elm Street'],
['Ronald', 'Heathmore', '892.345.3428', '436 Finley Avenue'],
['Frank', 'Burger', '925.541.7625', '662 South Dogwood Way'],
['Heather', 'Albrecht', '548.326.4584', '919 Park Place']]
:? 样式匹配姓后面的冒号,因此它不出现在结果列表中。如果 maxsplit 设置为 4 ,我们还可以从地址中获取到房间号:
>>> [re.split(":? ", entry, 4) for entry in entries]
[['Ross', 'McFluff', '834.345.1254', '155', 'Elm Street'],
['Ronald', 'Heathmore', '892.345.3428', '436', 'Finley Avenue'],
['Frank', 'Burger', '925.541.7625', '662', 'South Dogwood Way'],
['Heather', 'Albrecht', '548.326.4584', '919', 'Park Place']]
文字整理
sub() 替换字符串中出现的样式的每一个实例。这个例子证明了使用 sub() 来整理文字,或者随机化每个字符的位置,除了首位和末尾字符
>>> def repl(m):
... inner_word = list(m.group(2))
... random.shuffle(inner_word)
... return m.group(1) + "".join(inner_word) + m.group(3)
>>> text = "Professor Abdolmalek, please report your absences promptly."
>>> re.sub(r"(\w)(\w+)(\w)", repl, text)
'Poefsrosr Aealmlobdk, pslaee reorpt your abnseces plmrptoy.'
>>> re.sub(r"(\w)(\w+)(\w)", repl, text)
'Pofsroser Aodlambelk, plasee reoprt yuor asnebces potlmrpy.'
查找所有副词
findall() 匹配样式 所有 的出现,不仅是像 search() 中的第一个匹配。比如,如果一个作者希望找到文字中的所有副词,他可能会按照以下方法用 findall()
>>> text = "He was carefully disguised but captured quickly by police."
>>> re.findall(r"\w+ly\b", text)
['carefully', 'quickly']
查找所有的副词及其位置
如果想要获得比匹配文本更多的关于模式的所有匹配信息,则 finditer() 会很有用处因为它提供了 Match 对象而不是字符串。 继续前面的例子,如果某位作者想要查找某段文本中的所有副词 以及它们的位置,可以按以下方式使用 finditer():
>>> text = "He was carefully disguised but captured quickly by police."
>>> for m in re.finditer(r"\w+ly\b", text):
... print('%02d-%02d: %s' % (m.start(), m.end(), m.group(0)))
07-16: carefully
40-47: quickly
原始字符串标记
原始字符串记法 (r"text") 保持正则表达式正常。否则,每个正则式里的反斜杠('\') 都必须前缀一个反斜杠来转义。比如,下面两行代码功能就是完全一致的
>>> re.match(r"\W(.)\1\W", " ff ")
<re.Match object; span=(0, 4), match=' ff '>
>>> re.match("\\W(.)\\1\\W", " ff ")
<re.Match object; span=(0, 4), match=' ff '>
当需要匹配一个字符反斜杠,它必须在正则表达式中转义。在原始字符串记法,就是 r"\\"。否则就必须用 "\\\\",来表示同样的意思
>>> re.match(r"\\", r"\\")
<re.Match object; span=(0, 1), match='\\'>
>>> re.match("\\\\", r"\\")
<re.Match object; span=(0, 1), match='\\'>
写一个词法分析器
一个 词法器或词法分析器 分析字符串,并分类成目录组。 这是写一个编译器或解释器的第一步。
文字目录是由正则表达式指定的。这个技术是通过将这些样式合并为一个主正则式,并且循环匹配来实现的
from typing import NamedTuple
import re
class Token(NamedTuple):
type: str
value: str
line: int
column: int
def tokenize(code):
keywords = {'IF', 'THEN', 'ENDIF', 'FOR', 'NEXT', 'GOSUB', 'RETURN'}
token_specification = [
('NUMBER', r'\d+(\.\d*)?'), # 整数或小数
('ASSIGN', r':='), # 赋值运算符
('END', r';'), # 语句结束符
('ID', r'[A-Za-z]+'), # 标识符
('OP', r'[+\-*/]'), # 算术运算符
('NEWLINE', r'\n'), # 行结束符
('SKIP', r'[ \t]+'), # 跳过空格和制表符
('MISMATCH', r'.'), # 任何其他字符
tok_regex = '|'.join('(?P<%s>%s)' % pair for pair in token_specification)
line_num = 1
line_start = 0
for mo in re.finditer(tok_regex, code):
kind = mo.lastgroup
value = mo.group()
column = mo.start() - line_start
if kind == 'NUMBER':
value = float(value) if '.' in value else int(value)
elif kind == 'ID' and value in keywords:
kind = value
elif kind == 'NEWLINE':
line_start = mo.end()
line_num += 1
continue
elif kind == 'SKIP':
continue
elif kind == 'MISMATCH':
raise RuntimeError(f'{value!r} unexpected on line {line_num}')
yield Token(kind, value, line_num, column)
statements = '''
IF quantity THEN
total := total + price * quantity;
tax := price * 0.05;
ENDIF;
for token in tokenize(statements):
print(token)
该词法器产生以下的输出
Token(type='IF', value='IF', line=2, column=4)
Token(type='ID', value='quantity', line=2, column=7)
Token(type='THEN', value='THEN', line=2, column=16)
Token(type='ID', value='total', line=3, column=8)
Token(type='ASSIGN', value=':=', line=3, column=14)
Token(type='ID', value='total', line=3, column=17)
Token(type='OP', value='+', line=3, column=23)
Token(type='ID', value='price', line=3, column=25)
Token(type='OP', value='*', line=3, column=31)
Token(type='ID', value='quantity', line=3, column=33)
Token(type='END', value=';', line=3, column=41)
Token(type='ID', value='tax', line=4, column=8)
Token(type='ASSIGN', value=':=', line=4, column=12)
Token(type='ID', value='price', line=4, column=15)
Token(type='OP', value='*', line=4, column=21)
Token(type='NUMBER', value=0.05, line=4, column=23)
Token(type='END', value=';', line=4, column=27)
Token(type='ENDIF', value='ENDIF', line=5, column=4)
Token(type='END', value=';', line=5, column=9)