Skip to main content

6.2. re —正则表达式运算

源代码: Lib/re.py


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

要搜索的模式和字符串都可以是Unicode字符串以及8位字符串。但是,Unicode字符串和8位字符串不能混合:即,不能将Unicode字符串与字节模式匹配,反之亦然;类似地,当请求替换时,替换字符串必须与模式和搜索字符串具有相同的类型。

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

解决方案是使用Python的原始字符串符号表示正则表达式模式;在以 'r' 为前缀的字符串文字中,反斜杠不以任何特殊方式处理。因此,r"\n" 是包含 '\''n' 的双字符字符串,而 "\n" 是包含换行符的单字符字符串。通常模式将使用这个原始字符串符号在Python代码中表示。

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

6.2.1. 正则表达式语法

正则表达式(或RE)指定与其匹配的一组字符串;此模块中的函数允许您检查特定字符串是否与给定的正则表达式匹配(或者如果给定的正则表达式匹配特定的字符串,则归结为同一事物)。

正则表达式可以连接形成新的正则表达式;如果 AB 都是正则表达式,则 AB 也是正则表达式。通常,如果字符串 pA 匹配,而另一个字符串 qB 匹配,则字符串 pq 将匹配AB。除非 AB 包含低优先级操作; AB 之间的边界条件;或具有编号的组引用。因此,复杂表达式可以容易地从如这里描述的简单的基本表达式构建。有关正则表达式的理论和实现的细节,请参考上面引用的Friedl书,或几乎所有关于编译器构造的教科书。

以下是正则表达式格式的简要说明。有关更多信息和温和的演示,请咨询 正则表达式HOWTO

正则表达式可以包含特殊字符和普通字符。大多数普通字符,如 'A''a''0',是最简单的正则表达式;他们只是匹配自己。您可以连接普通字符,因此 last 匹配字符串 'last'。 (在本节的其余部分中,我们将在 this special style 中写入RE,通常不带引号,并匹配 'in single quotes' 的字符串。)

一些字符,如 '|''(',是特殊的。特殊字符代表普通字符的类,或者影响它们周围的正则表达式的解释。正则表达式模式字符串不能包含空字节,但可以使用 \number 表示法(例如 '\x00')指定空字节。

重复限定符(*+?{m,n} 等)不能直接嵌套。这避免了非贪婪修改符后缀 ? 的歧义,以及其他实现中的其他修饰符。为了对内部重复应用第二重复,可以使用括号。例如,表达式 (?:a{6})* 匹配六个 'a' 字符的任何倍数。

特殊字符是:

'.'

(点)在默认模式下,它匹配除换行符之外的任何字符。如果指定了 DOTALL 标志,则它匹配任何包括换行符的字符。

'^'

(插入符号)匹配字符串的开头,并且在 MULTILINE 模式下,每个换行符后立即匹配。

'$'

匹配字符串的结尾或刚好在字符串结尾处的换行符之前,并且在 MULTILINE 模式下也匹配换行符之前。 foo 匹配’foo’和’foobar’,而正则表达式 foo$ 只匹配’foo’。更有趣的是,在 'foo1\nfoo2\n' 中搜索 foo.$ 通常匹配’foo2’,而在 MULTILINE 模式中匹配’foo1’在 'foo\n' 中搜索单个 $ 将找到两个(空)匹配:一个刚好在换行之前,一个在字符串的结尾。

'*'

导致结果RE匹配前面RE的0次或更多次重复,尽可能多次重复。 ab* 将匹配’a’,’ab’或’a’,后跟任意数量的’b’。

'+'

导致结果RE匹配前面RE的1次或多次重复。 ab+ 将匹配’a’,后跟任何非零数字’b’;它将不匹配只是’a’。

'?'

导致结果RE匹配前面RE的0或1个重复。 ab? 将匹配’a’或’ab’。

*?+???

'*''+''?' 限定符均为 greedy;它们匹配尽可能多的文本。有时这种行为是不希望的;如果RE <.*><a> b <c> 匹配,则它将匹配整个字符串,而不仅仅是 <a>。在限定符之后添加 ? 使其以 non-greedyminimal 方式执行匹配;作为 few 字符尽可能匹配。使用RE <.*?> 将仅匹配 <a>

{m}

指定应匹配前一个RE的确切 m 副本;较少的匹配导致整个RE不匹配。例如,a{6} 将恰好匹配六个 'a' 字符,但不匹配五个。

{m,n}

导致结果RE从前一RE的 m 重复到 n 重复,尝试匹配尽可能多的重复。例如,a{3,5} 将匹配3到5个 'a' 字符。省略 m 指定零的下限,并且省略 n 指定无限上限。例如,a{4,}b 将匹配 aaaab 或一千个 'a' 字符,后跟 b,但不匹配 aaab。逗号可能不会省略,或者修饰符会与先前描述的形式混淆。

{m,n}?

导致结果RE从前一RE的 m 重复到 n 重复,试图尽可能匹配作为 few 重复。这是以前的限定词的非贪婪版本。例如,对于6个字符的字符串 'aaaaaa'a{3,5} 将匹配5个 'a' 字符,而 a{3,5}? 将只匹配3个字符。

'\'

可以转义特殊字符(允许您匹配 '*''?' 等字符),或发出特殊序列的信号;下面讨论特殊序列。

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

[]

用于表示一组字符。在一组:

  • 字符可以单独列出,例如。 [amk] 将匹配 'a''m''k'

  • 字符的范围可以通过给出两个字符并用 '-' 分隔它们来指示,例如 [a-z] 将匹配任何小写ASCII字母,[0-5][0-9] 将匹配从 0059 的所有两位数字,并且 [0-9A-Fa-f] 将匹配任何十六进制数字。如果 - 被转义(例如 [a\-z])或者如果它被放置为第一个或最后一个字符(例如 [a-]),则它将匹配文字 '-'

  • 特殊字符在集合中失去其特殊含义。例如,[(+*)] 将匹配任何字面字符 '(''+''*'')'

  • 字符类如 \w\S (下面定义)也可以在一个集合内被接受,虽然它们匹配的字符取决于 ASCIILOCALE 模式是否有效。

  • 不在一个范围内的字符可以由 complementing 匹配。如果集合的第一个字符是 '^',则集合中的所有 not 字符都将匹配。例如,[^5] 将匹配除 '5' 之外的任何字符,[^^] 将匹配除 '^' 之外的任何字符。如果它不是集合中的第一个字符,^ 没有特殊的意义。

  • 要匹配字符串 ']' 在集合内,在它前面加一个反斜杠,或将它放在集合的开头。例如,[()[\]{}][]()[{}] 都将匹配括号。

'|'

A|B,其中A和B可以是任意的RE,创建将匹配A或B的正则表达式。任意数量的RE可以由 '|' 以这种方式分离。这可以在组内使用(见下文)。当目标串被扫描时,由 '|' 分离的RE被从左到右尝试。当一个模式完全匹配时,接受该分支。这意味着一旦 A 匹配,B 将不会进一步测试,即使它将产生更长的整体匹配。换句话说,'|' 操作符从不贪婪。要匹配字面 '|',请使用 \|,或将其放在字符类中,如在 [|] 中。

(...)

匹配括号内的任何正则表达式,并指示组的开始和结束;可以在执行匹配之后检索组的内容,并且可以稍后在字符串中与下面描述的 \number 特殊序列匹配。要匹配字面值 '('')',请使用 \(\),或将它们包含在字符类中:[(] [)]

(?...)

这是一个扩展符号('(' 后面的 '?' 除外)。 '?' 后面的第一个字符决定了构造的含义和进一步的语法。扩展通常不创建新组; (?P<name>...) 是此规则的唯一例外。以下是当前支持的扩展。

(?aiLmsux)

(来自集合 'a''i''L''m''s''u''x' 的一个或多个字母。)组匹配空字符串;字母设置相应的标志:对于整个的 re.A (仅ASCII匹配),re.I (忽略大小写),re.L (依赖于区域),re.M (多行),re.S (点匹配所有)和 re.X 正则表达式。 (这些标志在 模块内容 中描述。)如果希望将标志作为正则表达式的一部分,而不是将 flag 参数传递给 re.compile() 函数,这将非常有用。应在表达式字符串中首先使用标志。

(?:...)

非捕获版本的常规括号。匹配括号中的任何正则表达式,但是在执行匹配或稍后在模式中引用时,将检索由 cannot 组匹配的子字符串。

(?imsx-imsx:...)

(来自集合 'i''m''s''x' 的零个或多个字母,可选地后跟 '-',接着是来自相同集合的一个或多个字母。)字母设置或去除相应的标志:re.I (忽略大小写),re.M -line),re.S (点匹配所有)和 re.X (verbose)。 (标志在 模块内容 中描述。)

3.6 新版功能.

(?P<name>...)

类似于常规括号,但组匹配的子字符串可通过符号组名称 name 访问。组名称必须是有效的Python标识符,每个组名称只能在正则表达式中定义一次。符号组也是编号组,就像组没有命名一样。

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

组“引用”的引用上下文

方法参考

在同一模式本身

  • (?P=quote) (如图所示)

  • \1

当处理匹配对象 m

  • m.group('quote')

  • m.end('quote') (等)

在传递给 re.sub()repl 参数的字符串中

  • \g<quote>

  • \g<1>

  • \1

(?P=name)

对指定组的反向引用;它匹配任何文本匹配的早期组名为 name

(?#...)

评论;括号的内容被简单地忽略。

(?=...)

匹配如果 ... 匹配下一个,但不消耗任何字符串。这被称为先行断言。例如,Isaac (?=Asimov) 将匹配 'Isaac ',只有后跟 'Asimov'

(?!...)

匹配如果 ... 下一个不匹配。这是一个负前瞻断言。例如,Isaac (?!Asimov) 将匹配 'Isaac ',只有它是 not 后跟 'Asimov'

(?<=...)

匹配如果字符串中的当前位置前面有在当前位置结束的 ... 匹配。这被称为 positive lookbehind assertion(?<=abc)def 将在 abcdef 中找到匹配项,因为后台将备份3个字符,并检查所包含的模式是否匹配。包含的模式只能匹配某个固定长度的字符串,这意味着允许 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 版更改: 增加了对固定长度的组引用的支持。

(?<!...)

匹配如果字符串中的当前位置前面没有 ... 匹配。这被称为 negative lookbehind assertion。与正的后向断言类似,包含的模式只能匹配某个固定长度的字符串。以负的后端断言开始的模式可能在被搜索的字符串的开头匹配。

(?(id/name)yes-pattern|no-pattern)

如果具有给定 idname 的组存在,则尝试与 yes-pattern 匹配,并且如果不存在,则尝试与 no-pattern 匹配。 no-pattern 是可选的,可以省略。例如,(<)?(\w+@\w+(?:\.\w+)+)(?(1)>|$) 是差的电子邮件匹配模式,其将与 '<user@host.com>' 以及 'user@host.com' 匹配,但是不与 '<user@host.com''user@host.com>' 匹配。

特殊序列由 '\' 和以下列表中的字符组成。如果普通字符不是ASCII数字或ASCII字母,则生成的RE将匹配第二个字符。例如,\$ 匹配字符 '$'

\number

匹配相同编号的组的内容。组从1开始编号。例如,(.+) \1 匹配 'the the''55 55',但不匹配 'thethe' (注意组后面的空格)。此特殊序列只能用于匹配前99个组中的一个。如果 number 的第一个数字是0,或者 number 是3个八进制数字,它不会被解释为组匹配,而是作为八进制值 number 的字符。在字符类的 '['']' 内,所有数字转义都被视为字符。

\A

仅匹配字符串的开头。

\b

匹配空字符串,但只在字的开头或结尾。字被定义为Unicode字母数字或下划线字符序列,因此字的结尾由空格或非字母数字,非下划线Unicode字符表示。注意,形式上,\b 被定义为 \w\W 字符之间的边界(反之亦然),或者 \w 和字符串的开始/结束之间的边界。这意味着 r'\bfoo\b' 匹配 'foo''foo.''(foo)''bar foo baz' 而不匹配 'foobar''foo3'

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

\B

匹配空字符串,但只有当它是 not 在单词的开头或结尾。这意味着 r'py\B' 匹配 'python''py3''py2',但不匹配 'py''py.''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] (但是标志影响整个正则表达式,因此在这种情况下使用显式 [^ \t\n\r\f\v] 可能是更好的选择)。

\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.6 版更改: '\' 和ASCII字母组成的未知转义现在是错误。

参见

掌握正则表达式

关于正则表达式的书由Jeffrey Friedl,O’Reilly出版。本书的第二版根本不再涵盖Python,但第一版涵盖了很好的编写良好的正则表达式模式。

6.2.2. 模块内容

模块定义了几个函数,常量和异常。一些函数是用于编译的正则表达式的全特征方法的简化版本。大多数非平凡应用程序总是使用编译的形式。

在 3.6 版更改: 标志常量现在是 RegexFlag 的实例,RegexFlagenum.IntFlag 的子类。

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)),但是在Python 3中这些是冗余的,因为匹配对于字符串是默认的Unicode(并且字符串不允许Unicode匹配)。

re.DEBUG

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

re.I
re.IGNORECASE

执行不区分大小写的匹配;表达式像 [A-Z] 也将匹配小写字母。这不受当前语言环境的影响,并如预期的那样工作于Unicode字符。

re.L
re.LOCALE

根据当前区域设置 \w\W\b\B\s\S。不鼓励使用此标志,因为语言环境机制是非常不可靠的,并且它一次只处理一个“文化”;你应该使用Unicode匹配,这是Unicode 3(str)模式中的默认值。此标志只能与字节模式一起使用。

在 3.6 版更改: re.LOCALE 只能与字节模式一起使用,并且与 re.ASCII 不兼容。

re.M
re.MULTILINE

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

re.S
re.DOTALL

使 '.' 特殊字符匹配任何字符,包括换行符;没有这个标志,'.' 会匹配任何 except 换行符。

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)

扫描通过 string 寻找正则表达式 pattern 产生匹配的第一个位置,并返回相应的 匹配对象。如果字符串中没有位置匹配模式,则返回 None;注意,这不同于在字符串中的某个点处找到零长度匹配。

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

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

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

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

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

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

3.4 新版功能.

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

通过 pattern 的发生拆分 string。如果在 pattern 中使用捕获括号,则模式中所有组的文本也将作为结果列表的一部分返回。如果 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’之前,’b’和’c’之间,以及’c’之后匹配0’x’,当前这些匹配被忽略。正确的行为(即分裂在空匹配和返回 ['', 'a', 'b', 'c', ''])将在未来的Python版本中实现,但由于这是一个向后不兼容的更改,FutureWarning 将同时被引发。

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

>>> 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 版更改: 添加了可选的flags参数。

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

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

返回 stringpattern 的所有非重叠匹配,作为字符串列表。 string 从左到右扫描,匹配以找到的顺序返回。如果模式中存在一个或多个组,请返回组列表;如果模式具有多个组,这将是元组的列表。结果中包含空匹配,除非它们接触另一个匹配的开头。

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

返回 iteratorstring 中的RE pattern 的所有非重叠匹配上产生 匹配对象string 从左到右扫描,匹配按找到的顺序返回。结果中包含空匹配,除非它们接触另一个匹配的开头。

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

返回通过用替换 repl 替换 stringpattern 的最左侧不重叠出现而获得的字符串。如果未找到模式,则 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 是一个函数,则对于 pattern 的每个非重叠发生都调用它。该函数接受单个匹配对象参数,并返回替换字符串。例如:

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

模式可以是字符串或RE对象。

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

在字符串型 repl 参数中,除了上述字符转义和反向引用之外,\g<name> 将使用由 (?P<name>...) 语法定义的名为 name 的组匹配的子字符串。 \g<number> 使用相应的组号;因此,\g<2> 等效于 \2,但在诸如 \g<2>0 的替换中不是含糊的。 \20 将被解释为对组20的引用,而不是对组2的引用,后面是文字字符 '0'。反向引用 \g<0> 替换由RE匹配的整个子字符串。

在 3.1 版更改: 添加了可选的flags参数。

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

在 3.6 版更改: pattern 中由 '\' 和ASCII字母组成的未知转义现在是错误。

自版本3.5以来已弃用,将在版本3.7中删除:未知的转义在repl包含’‘和ASCII字母现在提出一个弃用警告,将禁止在Python 3.7。

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

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

在 3.1 版更改: 添加了可选的flags参数。

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

re.escape(string)

除了ASCII字母,数字和 '_' 之外,模式中除去所有字符。如果您想匹配任何可能有正则表达式元字符的文字字符串,这将非常有用。

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

re.purge()

清除正则表达式高速缓存。

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

当传递到此处的某个函数的字符串不是有效的正则表达式(例如,它可能包含不匹配的括号)或在编译或匹配期间发生其他错误时引发的异常。如果字符串不包含匹配的模式,它从不是一个错误。错误实例具有以下附加属性:

msg

未格式化的错误消息。

pattern

正则表达式模式。

pos

编译失败的 pattern 的索引。

lineno

该行对应于 pos

colno

该列对应于 pos

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

6.2.3. 正则表达式对象

编译的正则表达式对象支持以下方法和属性:

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

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

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

可选参数 endpos 限制字符串搜索的距离;它将如同字符串是 endpos 个字符长,所以只有从 posendpos - 1 的字符将被搜索匹配。如果 endpos 小于 pos,则不会找到匹配;否则,如果 rx 是编译的正则表达式对象,则 rx.search(string, 0, 50) 等价于 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]])

如果 stringbeginning 处的零个或多个字符与此正则表达式匹配,则返回相应的 匹配对象。如果字符串与模式不匹配,返回 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]])

如果整个 string 匹配此正则表达式,则返回相应的 匹配对象。如果字符串与模式不匹配,则返回 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

编译RE对象的模式字符串。

6.2.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>)被相应组的内容替换。

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

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

返回匹配的一个或多个子组。如果有一个参数,结果是单个字符串;如果有多个参数,则结果是每个参数具有一个项的元组。没有参数,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.__getitem__(g)

这与 m.group(g) 相同。这使得更容易从匹配中访问单个组:

>>> m = re.match(r"(\w+) (\w+)", "Isaac Newton, physicist")
>>> m[0]       # The entire match
'Isaac Newton'
>>> m[1]       # The first parenthesized subgroup.
'Isaac'
>>> m[2]       # The second parenthesized subgroup.
'Newton'

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()      # Second group defaults to None.
('24', None)
>>> m.groups('0')   # Now, the second group defaults to '0'.
('24', '0')
match.groupdict(default=None)

返回包含匹配的所有 named 子组的字典,由子组名称键入。 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.start([group])
match.end([group])

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

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

注意,如果 group 匹配空字符串,m.start(group) 将等于 m.end(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 异常。

将从电子邮件地址中移除 remove_this 的示例:

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

对于匹配 m,返回2元组 (m.start(group), m.end(group))。注意,如果 group 没有贡献匹配,这是 (-1, -1)group 默认为零,整个匹配。

match.pos

pos 的值传递给 regex对象search()match() 方法。这是RE引擎开始寻找匹配的字符串的索引。

match.endpos

endpos 的值传递给 regex对象search()match() 方法。这是进入字符串的索引,超过该字符串RE引擎将不会去。

match.lastindex

最后匹配捕获组的整数索引,如果没有匹配的组,则为 None。例如,如果应用于字符串 'ab',则表达式 (a)b((a)(b))((ab)) 将具有 lastindex == 1,而如果应用于相同的字符串,则表达式 (a)(b) 将具有 lastindex == 2

match.lastgroup

最后匹配的捕获组的名称,如果该组没有名称,或者如果没有匹配任何组,则为 None

match.re

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

match.string

字符串传递给 match()search()

6.2.5. 正则表达式示例

6.2.5.1. 检查配对

在这个例子中,我们将使用以下帮助函数更加优雅地显示匹配对象:

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

假设你正在写一个扑克程序,其中玩家的手被表示为5个字符的字符串,每个字符代表一张卡,“a”代表王牌,“k”代表王牌,“q”代表王牌,“j” “t”表示10,“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() 在字符串中检查匹配的任何位置(这是Perl默认的做法)。

例如:

>>> 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() 匹配模式的 all 出现,而不仅仅是 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() 是有用的,因为它提供 匹配对象 而不是字符串。继续前面的例子,如果一个作者想要在某些文本中找到所有的副词 和他们的位置,他或她将以下面的方式使用 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")保持正则表达式正常。没有它,正则表达式中的每个反斜杠('\')都必须用另一个前缀来转义。例如,以下两行代码在功能上是相同的:

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

分词器或扫描器 分析字符串以对字符组进行分类。这是编写编译器或解释器的有用的第一步。

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

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)