6.2. re — 正则表达式

源代码: Lib/re.py

此模块提供与Perl中类似的正则表达式匹配操作。

搜索的模式和字符串都可以是Unicode字符串以及8比特字符串。不幸的事,在搜索时Unicode字符串和8-bit字符串不能混在一起:这是因为,你不能使用字节模式的去匹配Unicode字符串,反之亦然;相似的,当你准备做替换操作时,替换的字符串或模式也一定要和即将被替换的字符串或模式使用一样的类型

正则表达式使用反斜杠字符('\')表示特殊形式或允许使用特殊字符,而不调用其特殊含义。这与Python在字符串文字中用于相同目的的相同字符的使用相冲突;例如,为了匹配字面值反斜杠,可能必须将'\\\\'写为模式字符串,因为正则表达式必须是\\,每个反斜杠必须在常规Python字符串字面值内表示为\\

解决上面这种繁琐的处理方法是使用Python原始字符串符号的正则表达式模式;不以任何特殊方式在字符串字面值中以'r'前缀处理反斜杠。所以r"\n"包含 '\''n'两个字符, 但是"\n"只表示一个字符(即换行符)。通常在Python代码中,模式的表示使用这种原始字符串符号。

需要注意的是,大多数正则表达式操作都可用作编译的正则表达式上的模块级函数和方法。这些函数是快捷方式,不需要先编译正则表达式对象,但缺少一些微调参数。

6.2.1.正则表达式语法

正则表达式 (或 RE) 指定一组字符串匹配它;在此模块中的功能让您检查一下,如果一个特定的字符串匹配给定的正则表达式 (或给定的正则表达式匹配特定的字符串,可归结为同一件事)。

多个正则表达式通过相连形成新的正则表达式; 如果AB 两个都是正则表达式, 那么 AB 也是正则表达式。一般来说,如果字符串p匹配A且另一个字符串q匹配B,那么字符串pq将匹配AB。除非A或者B包含的优先级较低; 或 AB有边界条件;或者有被数组引用。因此,复杂的表达式可以轻松地从这里所描述的更简单的原始表达式构建。关于理论的细节及正则表达式的实现,请参阅上文引用的Friedl的书或任何一本有关编译器构造的教科书。

简要说明了正则表达式的格式如下。获得更多信息和优雅的展示,请参考Regular Expression HOWTO.

正则表达式可以包含特殊和普通字符。最普通的字符,如 'A''a',或 '0',是最简单的正则表达式;他们只是与自己相匹配。你可以连接普通的字符, 所以 last匹配字符串'last'.(在本章剩余部分,我们将会用this special style写正则表达式, 通常不用引号, 并且被匹配的字符串'in single quotes'.)

某些字符, 像 '|'或者'('是有特殊含义的。特殊字符要么表示某个类别的普通字符,要么影响它们周围的正则表达式如何解释。正则表达式模式字符串可能不含有空字节,但是可以用\number符号指定空字节,例如'\x00'.

特殊字符有:

'.'
(点号。)在默认模式下,匹配除换行符之外的任何字符。如果 DOTALL 标志被指定时,匹配任何字符包括换行符。
'^'
(脱字符号。)从字符串的开始匹配, 在 MULTILINE 模式下每个换行符后面立即开始匹配。
'$'
匹配字符串的结尾或只是之前换行符结尾的字符串,并在 多行 模式下也匹配在换行符之前。foo匹配'foo'和'foobar',而正则表达式foo$仅匹配'foo'。More interestingly, searching for foo.$ in 'foo1\nfoo2\n' matches ‘foo2’ normally, 但 ‘foo1’ 在 MULTILINE 模式下被匹配; searching for a single $ in 'foo\n' will find two (empty) matches: one just before the newline, and one at the end of the string.
'*'
匹配前面重复出现的正则表达式零次或多次,尽可能多的匹配。ab*将匹配'a','ab'或'a'后跟任意数量的'b'。
'+'
匹配前面重复出现的正则表达式1次或多次,尽可能多的匹配。ab+将匹配'a',后跟不少于一个的'b';它将不匹配只是'a'。
'?'
引起生成的RE匹配0个或1个前导的RE。ab?将匹配'a'或'ab'。
*?+???
'*''+''?'都是 贪婪模式它们尽可能匹配多次。有时我们不希望匹配多次;如果<.*>用来匹配 <a> b <c>, 它将会匹配所有字符串, 不只是<a>.加上?限定符将使得匹配为非贪婪模式或者minimal匹配; few尽可能少的字符被匹配。使用<.*?>将会仅匹配<a>.
{m}
精确的指定RE应该被匹配m次;少于m次将导致RE不会被匹配上。例如, a{6}将会精确匹配'a'字符6次,五次将不会被匹配。
{m,n}
引起生成的正则表达式匹配前导正则表达式的mn个重复,尝试匹配尽可能多的重复。例如,a{3,5}将匹配3到5个'a'字符。省略m指定零的下限,省略n指定无限上限。As an example, a{4,}b will match aaaab or a thousand 'a' characters followed by a b, but not aaab. 逗号不可以省略,否则该修改符将与前面的形式混淆。
{m,n}?
For example, on the 6-character string 'aaaaaa', a{3,5} will match 5 'a' characters, while a{3,5}?将只匹配3个字符。
'\'

消除特殊字符含义(允许匹配像'*', '?',等特殊字符), 或者发出特殊序列信号;特殊序列接下来将会讨论。

如果你不使用原始字符串来表达模式,记住Python也使用反斜杠作为字符串中的转义序列;如果Python解析器无法识别转义序列,则反斜杠和后续字符将包含在生成的字符串中。但是,如果Python会识别所产生的序列,反斜杠应该重复两次。这比较复杂和难以理解,因此强烈建议你为所有即使是最简单的表达式使用原始字符串。

[]

用来表示一个字符集合。在这个集合中:

  • 字符可以被单独罗列,例如:[amk] 会匹配 'a', 'm', 或 'k'.
  • 字符范围可以表明通过给予两个字符和分离他们的 '-'、 例如 [z] 将匹配任何小写字母的 ASCII 字母、 [0-5] [0-9] 将匹配所有两位数数字从 0059,和 [0-9A-Fa-f] 将都匹配任何十六进制数字。如果-被转义(例如,[a\-z]),或者将其放置为第一个或最后一个字符(例如,[a-]),它将匹配字面值'-'
  • 在集合内,特殊字符失去特殊意义。例如,[(+*)] 将匹配任何字符 '(''+''* ',或 '')''
  • \w or \S等字符类别也是可以被接受的(译者注:不会失去特殊意义),尽管匹配的这些字符取决于ASCII or LOCALE 模式是否被设置。
  • 不在一段范围之内的字符可以通过补集匹配。如果这个集合的第一个字符是'^', 那么所有在集合内的将会被匹配上。例如, [^5]将会配对除 '5'以外的任何字符,并且和[^^]将会匹配除'^'以外的任何字符。如果^不在集合的第一个位置那么它将没有特殊意义。
  • 想要在一个集合内匹配']',需要在它的前面使用一个反斜杠转义,或者在集合开头处将它替换。例如, [()[\]{}] and []()[{}] 都将会匹配一对括号。
'|'
A|B,其中A和B可以是任意RE,创建将匹配A或B的正则表达式。以这种方式,任意数量的RE可以由'|'分隔。这同样可以用在组里面。当扫描目标字符串时,从左到右尝试由'|'分隔的RE。当一个模式被完全匹配时,这个被匹配的模式就被接受。这意味着,一旦A匹配,则B将不会被进一步测试,即使它将产生更长的整体匹配。换句话说,'|'操作符从不贪婪。要匹配字面值'|',请使用\|或将其包含在字符类中,如[|]
(...)
匹配括号内的任何正则表达式,并指明组的开始和结束;可以在执行匹配之后检索组的内容,并且可以稍后在字符串中与\number特殊序列匹配,如下所述。匹配字面上的'(' or ')', 使用 \( or \), 或者把它们装入一个字符集中: [(] [)].
(?...)
这是一个扩展符号 (a '?'following a '(' 没有别的意义).'?'之后的第一个字符决定了意义和进一步的语法结构是什么.扩展通常不会创建一个新的组; (?P<name>...) 这个规则的唯一例外.以下是当前支持的扩展。
(?aiLmsux)

(一个或多个字母来自集合'a', 'i', 'L', 'm', 's', 'u', 'x'.)此组匹配空字符串;这些字母设定了相关的标识: re.A (仅匹配ASCII), re.I (不管大小写), re.L (locale dependent), re.M (多行), re.S (不匹配所有), and re.X (冗长的), 对于整个正则表达式。.(这些标志在Module Contents中描述。)如果你希望标识也是正则表达式的一部分那么这就是有用的, 而不是把flag 参数丢进 re.compile() 函数。

请注意,(?x)标志更改表达式的解析方式。它应使用在表达式字符串的开始,或一个或多个空白字符之后。如果在这个标志之前有非空白字符,结果是未定义的。

(?:...)
一个正则括号的不捕获版本.匹配括号中的任何正则表达式,但是匹配的子字符串不能在匹配后提取或在模式中引用。
(?P<name>...)

和正则括号相似, 但是这个组匹配到的子字符串可以通过符号组名称name进行访问.组名称必须是有效的Python标识符, 并且每个组名称在正则表达式中只能被定义一次(注:组名必须唯一).一个符号组也是一个带编号的组, 就好像这个组没有被命名一样.(注:除了原有的编号外再指定一个额外的别名).

可以在三种上下文中引用已命名的组。如果模式是 (?P<quote>['"]).*?(?P=quote) (例如:使用单引号或双引号来匹配一个被引用的字符串):

引用组的参考内容参考方法
in the same pattern itself
  • (?P=quote)(如图所示)
  • \1
当处理匹配对象m
  • m.group('quote')
  • m.end('quote')(etc.)
in a string passed to the repl argument of re.sub()
  • \g<quote>
  • \g<1>
  • \1
(?P=name)
对指定组的反向引用;它匹配任何名为name的早期组匹配的文本。
(?#...)
注释:忽略括号内的内容
(?=...)
匹配...匹配next,但不消耗任何字符串。This is called a lookahead assertion. 例如,Isaac (?= Asimov)会匹配'Isaac t5>,前提是它后面是'Asimov'
(?!...)
如果...与下一个不匹配,则匹配。This is a negative lookahead assertion. For example, Isaac (?!Asimov) will match 'Isaac ' only if it’s not followed by 'Asimov'.
(?<=...)

匹配如果字符串中的当前位置前面有结束于当前位置的...的匹配。这就被所谓的积极回顾后发断言(?<=abc)def will find a match in abcdef, since the lookbehind will back up 3 characters and check if the contained pattern matches. 包含的模式只能匹配某个固定长度的字符串,这意味着允许abca|b,但a*a{3,4}不是。注意,以肯定的后瞻断言开始的模式在被搜索的字符串的开头将不匹配;您很可能想使用search()函数,而不是match()函数:

>>> import re
>>> m = re.search('(?<=abc)def', 'abcdef')
>>> m.group(0)
'def'

本示例查看后面一个连字符的词:

>>> m = re.search('(?<=-)\w+', 'spam-egg')
>>> m.group(0)
'egg'

在版本3.5中已更改:添加了对固定长度的组引用的支持。

(?<!...)
匹配如果字符串中的当前位置前面没有匹配...这叫做 否定性回顾断言. Similar to positive lookbehind assertions, the contained pattern must only match strings of some fixed length. Patterns which start with negative lookbehind assertions may match at the beginning of the string being searched.
(?(id/name)yes-pattern|no-pattern)
如果存在给定id名称的组,以及no-pattern,则尝试与yes-patternno-pattern是可选的,可以省略。For example, (<)?(\w+@\w+(?:\.\w+)+)(?(1)>|$) is a poor email matching pattern, which will match with '<[email protected]>' as well as '[email protected]', but not with '<[email protected]' nor '[email protected]>'.

特殊序列由'\'和下面列表中的字符组成。If the ordinary character is not on the list, then the resulting RE will match the second character. 例如,\$匹配字符'$'

\number
匹配相对应组编号的内容。组号从1开始。例如,(.+) \1 匹配 'the the'或者 '55 55', 但是不会匹配'thethe' (注意在匹配上的组后有空格)。This special sequence can only be used to match one of the first 99 groups. If the first digit of number is 0, or number is 3 octal digits long, it will not be interpreted as a group match, but as the character with octal value number. 在字符类的'['']'内,所有数字转义都被视为字符。
\A
仅仅匹配字符串开头
\b

仅仅匹配单词的开头或结尾(译者注:不是字符串开头和结尾哦)。单词被定义为由Unicode字母数字或下划线组成的序列,因此单词的结尾由空格、非字母数字或非下划线Unicode字符表示。Note that formally, \b is defined as the boundary between a \w and a \W character (or vice versa), or between \w and the beginning/end of the string. This means that r'\bfoo\b' matches 'foo', 'foo.', '(foo)', 'bar foo baz' but not 'foobar' or 'foo3'.

默认情况下,Unicode字母数字是使用的字母数字,但可以使用ASCII标志更改。在字符范围内,\b表示退格字符,以与Python的字符串字面值兼容。

\B
匹配一个单词在开头或结尾的字符。This means that r'py\B' matches 'python', 'py3', 'py2', but not 'py', 'py.', or 'py!'. \B\b正好相反,因此字符字符是Unicode字母数字或下划线,但可以使用ASCII旗。
\d
对于Unicode(str)模式:
匹配任何Unicode十进制数字(即,Unicode字符类别[Nd]中的任何字符)。这包括[0-9]以及许多其他数字字符。如果使用ASCII标志只有[0-9]匹配(但该标志影响整个正则表达式,因此在这种情况下使用显式[0-9]可能是更好的选择)。
对于8位(字节)模式:
匹配任何十进制数字;这相当于[0-9]
\D
匹配任何不是Unicode十进制数字的字符。这与\d相反。如果使用ASCII标志,这相当于[^0-9](但标志影响整个正则表达式,因此在这种情况下使用显式[^0-9]可能是更好的选择)。
\s
对于Unicode(str)模式:
匹配Unicode空格字符(其中包括[ \ t \ n \ r \ f \ v],以及许多其他字符,例如在许多语言中由排版规则强制的不间断空间)。如果使用ASCII标志,只有[ \ t \ n \ r \ f \ v] (但是该标志影响整个正则表达式,因此在这种情况下使用显式[ \ t \ n \ r \ f \ v]可能是更好的选择)。
对于8位(字节)模式:
匹配ASCII字符集中被视为空格的字符;这等同于[ \ t \ n \ r \ f \ v]
\S
匹配任何不是Unicode空格字符的字符。这与\s相反。如果使用ASCII标志,这相当于[^ \ t \ n \ r \ f \ v] t3>(但是该标志影响整个正则表达式,因此在这种情况下使用显式[^ \ t \ n \ r \ f \ v] / t6>可能是更好的选择)。
\w
对于Unicode(str)模式:
匹配Unicode字符;这包括可以作为任何语言中的单词的一部分的大多数字符,以及数字和下划线。如果使用ASCII标志,只有[a-zA-Z0-9_]匹配(但是标志影响整个正则表达式, [a-zA-Z0-9_]可能是更好的选择)。
对于8位(字节)模式:
匹配ASCII字符集中被视为字母数字的字符;这相当于[a-zA-Z0-9_]
\W
匹配任何不是Unicode字符的字符。这与\w相反。如果使用ASCII标志,这相当于[^a-zA-Z0-9_](但标志影响整个正则表达式,因此在这种情况下使用显式的[^a-zA-Z0-9_]可能是更好的选择)。
\Z
只在字符串的结尾处进行匹配

由正则表达式分析器也接受大多数支持通过 Python 字符串的标准转义:

\a      \b      \f      \n
\r      \t      \u      \U
\v      \x      \\

(请注意,\b用于表示字边界,并且仅表示字符类中的“退格”。)

'\u''\U'转义序列仅在Unicode模式中可识别。在字节模式中,它们不被特别处理。

八进制转义包含在有限的形式中。如果第一个数字为0,或者如果有三个八进制数字,则被认为是八进制转义。否则,它是组引用。字符串文本总是八进制转义顶多是三个数字的长度。

在版本3.3中已更改:已添加'\u''\U'转义序列。

自版本3.5起已弃用,将在版本3.6中删除:'\'和ASCII字母组成的未知转义现在引发弃用警告,并将在Python 3.6中禁止。

请参见

Mastering Regular Expressions 《精通正则表达式》
O’Reilly 该书第 2 版不再涵盖Python内容,但其第 1 版中涵盖了很多写得不错、非常详细的正则表达式模式。

6.2.2.模块内容

模块定义了几个函数、 常量和一个异常。某些函数是编译正则表达式全特性方法的简化版本。大多数复杂应用程序总是使用已编译的形式。

re.compile(pattern, flags=0)

将正则表达式模式编译成一个正则表达式对象,匹配时可以调用它的 match()search() 方法,如下所述。

可以通过指定flags 值修改表达式的行为。值可以是任何以下变量, 组合使用 OR ( | 运算符).

序列

prog = re.compile(pattern)
result = prog.match(string)

等同于

result = re.match(pattern, string)

但表达式在单个程序中多次使用时, 使用re.compile() 和保存生成的正则表达式对象重用效率更高。

传递到re.compile()的最新模式的编译版本和模块级匹配函数都被缓存,因此一次只使用少数正则表达式的程序不需要担心编译正则表达式。

re.A
re.ASCII

\w\W\b\B\d\D\s\S执行纯ASCII匹配而不是完全Unicode匹配。这只对Unicode模式有意义,对于字节模式将被忽略。

注意,为了向后兼容,re.U标志仍然存在(以及其同义词re.UNICODE及其嵌入的对应(?u)

re.DEBUG

显示调试信息编译的表达式。

re.I
re.IGNORECASE

执行不区分大小写的匹配; 如 [A-Z]也将匹配为小写字母.这不受当前语言环境的影响,适用于预期的Unicode字符。

re.L
re.LOCALE

\w\W\b\B\s\S,具体取决于当前语言环境。不鼓励使用此标志,因为语言环境机制非常不可靠,并且它一次只处理一个“文化”;您应该使用Unicode匹配,这是Unicode 3(str)模式中的默认值。这个标志只有字节模式才有意义。

自版本3.5起已弃用,将在版本3.6中删除:已弃用使用字符串模式或re.ASCIIre.LOCALE

re.M
re.MULTILINE

指定时,模式字符'^'在字符串开头和每行开头(紧跟每个换行符后)匹配;并且模式字符'$'在字符串的末尾和每行的末尾(紧接在每个换行符之前)匹配。默认情况下,'^'仅在字符串开头匹配,'$'仅在字符串结尾处,字符串的结尾。

re.S
re.DOTALL

制作'.'特殊字符匹配任何字符,包括换行符;没有此标志,'.'将匹配除了换行符之外的任何

re.X
re.VERBOSE

此标志允许您编写正则表达式看起来更好,更可读,通过允许您在视觉上分离模式的逻辑节并添加注释。模式中的空格被忽略,除非在字符类中或前面有一个未转义的反斜杠。当一行包含不属于字符类的#,并且没有未转义的反斜杠时,从最左边的#到该行末尾的所有字符忽略。

这意味着匹配十进制数的两个以下正则表达式对象在功能上是相等的:

a = re.compile(r"""\d +  # the integral part
                   \.    # the decimal point
                   \d *  # some fractional digits""", re.X)
b = re.compile(r"\d+\.\d*")
re.search(pattern, string, flags=0)

扫描字符串查找正则表达式模式产生匹配的第一个位置,并返回相应的match object如果字符串中没有位置与模式匹配,则返回None;请注意,这不同于在字符串中的某个点找到零长度匹配。

re.match(pattern, string, flags=0)

如果字符串开头的零个或多个字符与正则表达式模式相匹配,则返回相应的match object如果字符串与模式不匹配,则返回None;注意,这不同于零长度匹配。

请注意,即使在MULTILINE模式下,re.match()仅在字符串开头匹配,而不在每行开头匹配。

如果您要在string中找到匹配项,请改用search()(另请参阅search() vs. match())。

re.fullmatch(pattern, string, flags=0)

如果整个字符串与正则表达式模式相匹配,则返回相应的match object如果字符串与模式不匹配,则返回None;注意,这不同于零长度匹配。

版本3.4中的新功能。

re.split(pattern, string, maxsplit=0, flags=0)

字符串拆分的模式的匹配项。如果在模式中使用捕获括号,则然后也作为结果列表的一部分返回的文本模式中的所有组。如果maxsplit不为零,顶多maxsplit分裂发生,并且该字符串的其余部分将作为列表的最后一个元素返回。

>>> re.split('\W+', 'Words, words, words.')
['Words', 'words', 'words', '']
>>> re.split('(\W+)', 'Words, words, words.')
['Words', ', ', 'words', ', ', 'words', '.', '']
>>> re.split('\W+', 'Words, words, words.', 1)
['Words', 'words, words.']
>>> re.split('[a-f]+', '0a3B9', flags=re.IGNORECASE)
['0', '3', '9']

如果在分离器有捕获组,它匹配字符串的开头,结果将启动与空字符串。同样对于字符串的末尾:

>>> re.split('(\W+)', '...words, words...')
['', '...', 'words', ', ', 'words', '...', '']

这样的话, 分割的组分也会在结果列表中存在。

split()当前没有在空模式匹配时分割字符串。举个例子:

>>> re.split('x*', 'axbc')
['a', 'bc']

即使'x*'也匹配'a'之前的0'x','b'和'c'之间和'c'之后,当前这些匹配被忽略。正确的行为(即分割为空匹配并返回[“, 'a', 'b', 'c' ''])将会在未来的Python版本中实现,但由于这是一个向后兼容的更改,因此会出现FutureWarning同时。

只能匹配空字符串的模式当前从不拆分字符串。由于这与预期的行为不匹配,因此将从Python 3.5开始提升ValueError

>>> re.split("^$", "foo\n\nbar\n", flags=re.M)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  ...
ValueError: split() requires a non-empty pattern match.

在版本3.1中已更改:添加了可选标志参数。

在版本3.5中更改:在可能匹配空字符串的模式上拆分现在引发警告。只能匹配空字符串的模式现在被拒绝。

re.findall(pattern, string, flags=0)

作为一个字符串列表,在字符串中,返回所有非重叠匹配的模式The string是从左到右扫描的,所以匹配的内容是按照该顺序来的如果模式中存在一个或多个组,请返回组列表;如果模式具有多个组,这将是元组的列表。Return all non-overlapping matches of pattern in string, as a list of strings. The string是从左到右扫描的,所以匹配的内容是按照该顺序来的If one or more groups are present in the pattern, return a list of groups; this will be a list of tuples if the pattern has more than one group. Empty matches are included in the result unless they touch the beginning of another match.

re.finditer(pattern, string, flags=0)

string中为RE 模式的所有非重叠匹配返回iterator产生match objects字符串是从左到右扫描的,匹配按照发现的顺序返回。空匹配包含在结果中,除非他们接触到另一场匹配的开头。

re.sub(pattern, repl, string, count=0, flags=0)

string中最左侧非重叠出现的pattern替换为repl,返回所获得的字符串。如果pattern没有被找到, string不变。repl 可以是一个字符串或一个函数;如果是一个字符串, 则会处理每个反斜杠转义。即,\n被转换为单个换行符,\r被转换为回车符,依此类推。未知的转义(例如\&)将保留。反向引用,例如\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是一个函数,它被呼吁每个非重叠模式发生。该函数采用单个匹配对象作为参数,并返回替换字符串。举个例子:

>>> 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可以是一个字符串或一个RE对象。

可选参数count是要替换的模式出现的最大数量;count必须是非负整数。如果省略或为零,则所有出现都将被替换。只有当不与上一个匹配相邻时,才替换模式的空匹配,因此sub('x*', '-', 'abc')返回'-a-b-c-'

In string-type repl arguments, in addition to the character escapes and backreferences described above, \g<name> will use the substring matched by the group named name, as defined by the (?P<name>...) syntax. \g<number>使用相应的组号;因此,\g<2>等效于\2,但在替换例如\g<2>0中不含糊。\20将被解释为对组20的引用,而不是对后面跟着字面值字符'0'的组2的引用。backreference \g<0>替换由RE匹配的整个子字符串。

在版本3.1中已更改:添加了可选标志参数。

在版本3.5中已更改:不匹配的组将替换为空字符串。

自版本3.5起已弃用,将在版本3.6中删除:未知转义由'\'和ASCII字母组成现在引发弃用警告并将在Python 3.6中禁止。

re.subn(pattern, repl, string, count=0, flags=0)

执行与sub()相同的操作,但返回一个元组(new_string, number_of_subs_made)

在版本3.1中已更改:添加了可选标志参数。

在版本3.5中已更改:不匹配的组将替换为空字符串。

re.escape(string)

退出模式中除ASCII字母,数字和'_'之外的所有字符。这是有用的如果你想匹配一个任意的文本字符串,在它可能包含正则表达式元字符。

在版本3.3中更改: '_'字符不再转义。

re.purge()

清除正则表达式缓存。

exception re.error(msg, pattern=None, pos=None)

当一个字符串传递给这里的函数之一时引发的异常不是有效的正则表达式 (例如,它可能包含不匹配的括号) 或其他一些错误在编译或匹配过程中发生的时。如果一个字符串包含不匹配的一种模式,它永远不会是一个错误。错误实例具有以下附加属性:

msg

未格式化的错误消息。

pattern

正则表达式模式。

pos

模式的索引,其中编译失败。

lineno

对应于pos的行。

colno

pos对应的列。

在3.5版中已更改:添加了其他属性。

6.2.3.正则表达式对象

已编译的正则表达式对象支持下列方法和属性︰

regex.search(string[, pos[, endpos]])

扫描string 寻找正则表达式产生匹配后的第一个位置 , 然后返回一个相对应的 match object.如果字符串中没有位置与模式匹配,则返回None;请注意,这不同于在字符串中的某个点找到零长度匹配。

可选的第二个参数 pos 给索引在字符串中搜索在哪里开始;它默认为 0这不完全等同于切割字符串; '^'模式字符在字符串的真实开头和换行之后的位置匹配,但不一定在搜索开始的索引处。

The optional parameter endpos limits how far the string will be searched; it will be as if the string is endpos characters long, so only the characters from pos to endpos - 1 will be searched for a match. 如果endpos小于pos,则不会找到匹配项;如果rx是编译的正则表达式对象,rx.search(string, 0, 50) t6>等效于rx.search(string [:50], 0)

>>> pattern = re.compile("d")
>>> pattern.search("dog")     # Match at index 0
<_sre.SRE_Match object; span=(0, 1), match='d'>
>>> pattern.search("dog", 1)  # No match; search doesn't include the "d"
regex.match(string[, pos[, endpos]])

如果字符串开始处的零个或多个字符与此正则表达式匹配,则返回相应的match object如果返回 None ,则该字符串不匹配模式;注意这不同于一个零字节长度的匹配。

可选的posendpos参数具有与search()方法相同的含义。

>>> pattern = re.compile("o")
>>> pattern.match("dog")      # No match as "o" is not at the start of "dog".
>>> pattern.match("dog", 1)   # Match as "o" is the 2nd character of "dog".
<_sre.SRE_Match object; span=(1, 2), match='o'>

如果您要在string中找到匹配项,请改用search()(另请参阅search() vs. match())。

regex.fullmatch(string[, pos[, endpos]])

如果整个字符串与此正则表达式匹配,则返回相应的match object如果字符串与模式不匹配,则返回None;注意,这不同于零长度匹配。

可选的posendpos参数具有与search()方法相同的含义。

>>> pattern = re.compile("o[gh]")
>>> pattern.fullmatch("dog")      # No match as "o" is not at the start of "dog".
>>> pattern.fullmatch("ogre")     # No match as not the full string matches.
>>> pattern.fullmatch("doggie", 1, 3)   # Matches within given limits.
<_sre.SRE_Match object; span=(1, 3), match='og'>

版本3.4中的新功能。

regex.split(string, maxsplit=0)

split()函数相同,使用编译的模式。

regex.findall(string[, pos[, endpos]])

类似于 findall() 的功能,使用已编译的模版,但也接受可选的 posendpos 参数限制搜索区域像 match ()

regex.finditer(string[, pos[, endpos]])

类似于 finditer() 的功能,使用已编译的模版,但也接受可选的 posendpos 参数像 match ()限制搜索区域。

regex.sub(repl, string, count=0)

sub()函数相同,使用编译的模式。

regex.subn(repl, string, count=0)

subn()函数相同,使用编译的模式。

regex.flags

正则表达式匹配的标志。这是给予compile(),任何(?...)的标志的组合,内联标志,以及如果模式是Unicode字符串,则隐式标志,例如UNICODE

regex.groups

捕获模式中的组数。

regex.groupindex

将由(?P<id>)定义的任何符号组名称映射到组号的字典。这本词典是空的如果在模式中使用了无符号的组。

regex.pattern

模式字符串中从中重新对象的编译。

6.2.4.Match 对象

Match对象总是有一个True的布尔值。由于match()search() 在没有匹配上时返回 None。你可以用简单的 if语句测试是否有match对象。

match = re.search(pattern, string)
if match:
    process(match)

Match 对象支持下列方法和属性︰

match.expand(template)

返回通过对模板字符串模板执行反斜杠替换获得的字符串,如sub()方法所做。\n之类的转移符会被转换成对应的字符,数字型的反向捕获(\1, \2)和命名反向捕获(\g<1>,\g<name>)会被替换为相应捕获组中的内容。

在版本3.5中已更改:不匹配的组将替换为空字符串。

match.group([group1, ...])

返回Match对象的一个或多个子组。如果有一个参数,结果是单个字符串;如果有多个参数,结果是每个参数有一个项的元组。如果没有参数, group1默认为零 (整场比赛返回)。如果groupN参数为零,则相应的返回值是整个匹配字符串;如果它在包含范围[1..99]中,则是与相应的括号组匹配的字符串。如果组号为负数或大于模式中定义的组数,则会引发IndexError异常。如果模式的一部分中包含不匹配的组,则相应的结果为None如果一组包含在模式匹配多次的一部分,则返回最后一场比赛。

>>> m = re.match(r"(\w+) (\w+)", "Isaac Newton, physicist")
>>> m.group(0)       # The entire match
'Isaac Newton'
>>> m.group(1)       # The first parenthesized subgroup.
'Isaac'
>>> m.group(2)       # The second parenthesized subgroup.
'Newton'
>>> m.group(1, 2)    # Multiple arguments give us a tuple.
('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")  # Matches 3 times.
>>> m.group(1)                        # Returns only the last match.
'c3'
match.groups(default=None)

返回包含所有匹配到的子组的元组, 从1到模式中的所有组。默认参数用于未参与匹配的组;它默认为None

举个例子:

>>> m = re.match(r"(\d+)\.(\d+)", "24.1632")
>>> m.groups()
('24', '1632')

如果我们使小数点和一切在它以后可选,并不是所有的组可能会参加比赛。这些组默认为None,除非提供默认参数:

>>> m = re.match(r"(\d+)\.?(\d+)?", "24")
>>> m.groups()      # Second group defaults to None.
('24', None)
>>> m.groups('0')   # Now, the second group defaults to '0'.
('24', '0')
match.groupdict(default=None)

返回一个有别名的组的匹配子组的字典(没有别名的子组不包含在内)。键为子组名,值为子串。默认参数用于未参与匹配的组;它默认为None举个例子:

>>> m = re.match(r"(?P<first_name>\w+) (?P<last_name>\w+)", "Malcolm Reynolds")
>>> m.groupdict()
{'first_name': 'Malcolm', 'last_name': 'Reynolds'}
match.start([group])
match.end([group])

返回由group匹配的子字符串的开头和结尾的索引; 默认为零(表示整个匹配的子字符串)。如果存在,但没有对匹配作出贡献,则返回-1对于匹配对象m和确实有助于匹配的组g,组g匹配的子串(等效于m.group(g)

m.string[m.start(g):m.end(g)]

请注意,如果group匹配一个空字符串,m.start(group)将等于m.end(group)For example, after m = re.search('b(c?)', 'cba'), m.start(0) is 1, m.end(0) is 2, m.start(1) and m.end(1) are both 2, and m.start(2) raises an IndexError exception.

将删除的电子邮件地址remove_this的示例:

>>> email = "tony@tiremove_thisger.net"
>>> m = re.search("remove_this", email)
>>> email[:m.start()] + email[m.end():]
'[email protected]'
match.span([group])

对于匹配m,返回2元组(m.start(group), m.end(group)) / t1>。请注意,如果没有参与匹配,则为( - 1, -1)的默认值为零,整场比赛。

match.pos

传递到regex objectsearch()match()方法的pos这是重新引擎开始寻找匹配的字符串中的索引。

match.endpos

传递到regex objectsearch()match()方法的endpos这是之外,重新引擎不会将字符串中的索引。

match.lastindex

上一个匹配捕获组的整数索引,或None(如果没有匹配任何组)。For example, the expressions (a)b, ((a)(b)), and ((ab)) will have lastindex == 1 if applied to the string 'ab', while the expression (a)(b) will have lastindex == 2, if applied to the same string.

match.lastgroup

上次匹配的捕获组的名称,或None如果组没有名称,或者没有匹配任何组。

match.re

match()search()的正则表达式对象产生此匹配实例。

match.string

match()或者search()匹配的字符串。

6.2.5.正则表达式实例

6.2.5.1.检查对

在这个例子中,我们将使用下面的 helper 函数去更优雅地显示 match 对象:

def displaymatch(match):
    if match is None:
        return None
    return '<Match: %r, groups=%r>' % (match.group(), match.groups())

假设您要编写一个球员手为 5 个字符的字符串代表与代表一张卡,每个字符的扑克程序"a"ace、"k"为国王、 王后、 杰克的"j"、"t"为 10,"q"和"2"至"9"代表卡具有此值。

若想查看给定字符串是否为一手有效牌的话,可履行以下操作:

>>> valid = re.compile(r"^[a2-9tjqk]{5}$")
>>> displaymatch(valid.match("akt5q"))  # Valid.
"<Match: 'akt5q', groups=()>"
>>> displaymatch(valid.match("akt5e"))  # Invalid.
>>> displaymatch(valid.match("akt"))    # Invalid.
>>> displaymatch(valid.match("727ak"))  # Valid.
"<Match: '727ak', groups=()>"

最后一只手,"727ak"包含一对或两张相同值的卡。若想以正则表达式匹配它的话,可使用如下反向引用:

>>> pair = re.compile(r".*(.).*\1")
>>> displaymatch(pair.match("717ak"))     # Pair of 7s.
"<Match: '717', groups=('7',)>"
>>> displaymatch(pair.match("718ak"))     # No pairs.
>>> displaymatch(pair.match("354aa"))     # Pair of aces.
"<Match: '354aa', groups=('a',)>"

要找出该对所包含的卡,可以按以下方式使用匹配对象的group()方法:

>>> pair.match("717ak").group(1)
'7'

# Error because re.match() returns None, which doesn't have a group() method:
>>> 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)
'a'

6.2.5.2.模拟scanf()

Python当前不具有scanf()的等价物。正则表达式通常比scanf()格式字符串更强大,但也更冗长。下表提供了scanf()格式令牌和正则表达式之间的一些或多或少的等效映射。

scanf()令牌正则表达式
%c.
%5c.{5}
%d[-+]?\d+
%e%E%f%g[-+]?(\d+(\.\d*)?|\.\d+)([eE][-+]?\d+)?
%i[-+]?(0[xX][\dA-Fa-f]+|0[0-7]*|\d+)
%o[-+]?[0-7]+
%s\S+
%u\d+
%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

6.2.5.3. search() vs. match()

Python提供了两种基于正则表达式的基本操作:re.match()仅在字符串开头检查匹配,而re.search()

举个例子:

>>> re.match("c", "abcdef")    # No match
>>> re.search("c", "abcdef")   # Match
<_sre.SRE_Match object; span=(2, 3), match='c'>

'^'开头的正则表达式可与search()一起使用,以限制字符串开头处的匹配:

>>> re.match("c", "abcdef")    # No match
>>> re.search("^c", "abcdef")  # No match
>>> re.search("^a", "abcdef")  # Match
<_sre.SRE_Match object; span=(0, 1), match='a'>

但请注意,在MULTILINE模式match()只匹配字符串开头,而使用search() '^'将在每行的开头匹配。

>>> re.match('X', 'A\nB\nX', re.MULTILINE)  # No match
>>> re.search('^X', 'A\nB\nX', re.MULTILINE)  # Match
<_sre.SRE_Match object; span=(4, 5), match='X'>

6.2.5.4.制作电话簿

split()将字符串拆分为由传递模式分隔的列表。该方法是将文本数据转换为数据结构,可以轻松地阅读和修改由 Python 作为显示在下面的示例创建一个电话簿非常宝贵。

首先,这里是输入。通常它可能来自一个文件,在这里我们使用的三重引号的字符串语法:

>>> 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']]

:?模式匹配姓氏后的冒号,以便它不会出现在结果列表中。使用4maxsplit,我们可以将房屋号码与街道名称分开:

>>> [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']]

6.2.5.5.文本显示

sub()用字符串或函数的结果替换模式的每一次出现。此示例演示如何使用带有“munge”文本的函数的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.'

6.2.5.6.查找所有副词

findall()匹配模式的所有个匹配项,而不仅仅是search()匹配的第一个匹配项。例如,如果一个人是作家,并且希望在某些文本中找到所有副词,他或她可以以下列方式使用findall()

>>> text = "He was carefully disguised but captured quickly by police."
>>> re.findall(r"\w+ly", text)
['carefully', 'quickly']

6.2.5.7.查找所有副词及其位置

如果需要有关匹配文本的所有匹配模式的信息,finditer()很有用,因为它提供match objects而不是字符串。继续前面的例子,如果一个作家想要在某些文本中找到所有的副词和他们的位置,他或她将使用finditer()

>>> text = "He was carefully disguised but captured quickly by police."
>>> for m in re.finditer(r"\w+ly", text):
...     print('%02d-%02d: %s' % (m.start(), m.end(), m.group(0)))
07-16: carefully
40-47: quickly

6.2.5.8.原始字符串符号

原始字符串符号(r"text")保持正则表达式正常。没有它,正则表达式中的每个反斜杠('\')必须以另一个前缀为前缀,以转义它。For example, the two following lines of code are functionally identical:

>>> re.match(r"\W(.)\1\W", " ff ")
<_sre.SRE_Match object; span=(0, 4), match=' ff '>
>>> re.match("\\W(.)\\1\\W", " ff ")
<_sre.SRE_Match object; span=(0, 4), match=' ff '>

当想要匹配文字反斜杠时,必须在正则表达式中先转义。使用原始字符串符号,这意味着r"\\"没有原始字符串符号,必须使用"\\\\",使以下代码行功能相同:

>>> re.match(r"\\", r"\\")
<_sre.SRE_Match object; span=(0, 1), match='\\'>
>>> re.match("\\\\", r"\\")
<_sre.SRE_Match object; span=(0, 1), match='\\'>

6.2.5.9.编写一个Tokenizer

分词器或扫描器分析字符串以对字符组进行分类。This is a useful first step in writing a compiler or interpreter.

使用正则表达式指定文本类别。该技术是将这些组合成单个主正则表达式并循环连续匹配:

import collections
import re

Token = collections.namedtuple('Token', ['typ', 'value', 'line', 'column'])

def tokenize(code):
    keywords = {'IF', 'THEN', 'ENDIF', 'FOR', 'NEXT', 'GOSUB', 'RETURN'}
    token_specification = [
        ('NUMBER',  r'\d+(\.\d*)?'),  # Integer or decimal number
        ('ASSIGN',  r':='),           # Assignment operator
        ('END',     r';'),            # Statement terminator
        ('ID',      r'[A-Za-z]+'),    # Identifiers
        ('OP',      r'[+\-*/]'),      # Arithmetic operators
        ('NEWLINE', r'\n'),           # Line endings
        ('SKIP',    r'[ \t]+'),       # Skip over spaces and tabs
        ('MISMATCH',r'.'),            # Any other character
    ]
    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(kind)
        if kind == 'NEWLINE':
            line_start = mo.end()
            line_num += 1
        elif kind == 'SKIP':
            pass
        elif kind == 'MISMATCH':
            raise RuntimeError('%r unexpected on line %d' % (value, line_num))
        else:
            if kind == 'ID' and value in keywords:
                kind = value
            column = mo.start() - line_start
            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(typ='IF', value='IF', line=2, column=4)
Token(typ='ID', value='quantity', line=2, column=7)
Token(typ='THEN', value='THEN', line=2, column=16)
Token(typ='ID', value='total', line=3, column=8)
Token(typ='ASSIGN', value=':=', line=3, column=14)
Token(typ='ID', value='total', line=3, column=17)
Token(typ='OP', value='+', line=3, column=23)
Token(typ='ID', value='price', line=3, column=25)
Token(typ='OP', value='*', line=3, column=31)
Token(typ='ID', value='quantity', line=3, column=33)
Token(typ='END', value=';', line=3, column=41)
Token(typ='ID', value='tax', line=4, column=8)
Token(typ='ASSIGN', value=':=', line=4, column=12)
Token(typ='ID', value='price', line=4, column=15)
Token(typ='OP', value='*', line=4, column=21)
Token(typ='NUMBER', value='0.05', line=4, column=23)
Token(typ='END', value=';', line=4, column=27)
Token(typ='ENDIF', value='ENDIF', line=5, column=4)
Token(typ='END', value=';', line=5, column=9)