# 6.2. `re` — 正则表达式

源代码: Lib/re.py

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

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

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

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

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

## 6.2.1.正则表达式语法

正则表达式 （或 RE） 指定一组字符串匹配它;在此模块中的函数，可检查特定字符串是否匹配给定的正则表达式 （或给定的正则表达式匹配特定的字符串，两个说法是一回事）。

多个正则表达式可以连起来，形成新的正则表达式; 若A 、 B 都是正则表达式, 则 AB 也是正则表达式。通常，若字符串 p 匹配 A ，且另一字符串 q 匹配 B， 那么字符串 pq 将会匹配 AB。 除非：A或者B含有低优先级的操作; 或 A和B之间有边界条件;或者有被数组引用。因此，复杂的表达式可以很容易地从简单的基本表达式构建，就像这里描述的那样。有关正则表达式的理论和实现的详细信息，请参阅上面引用的Friedl书或几乎所有关于编译器构造的教科书。

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

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

某些字符, 像 `'|'`或者`'('`具有特殊含义。特殊字符或者表示普通字符类，或者影响正则表达式如何解释。正则表达式模式字符串可能不含有null字节，但是可以用`\number`符号指定空字节，例如`'\x00'`.

特殊字符是：

`'.'`
  （点。）默认模式下，匹配换行符以外的任何字符。若 `DOTALL` 标志被指定，则它匹配换行符在内的任何字符。
`'^'`
  （尖）。从字符串的开始匹配, 在 `MULTILINE` 模式下每个换行符后面立即开始匹配。
`'$'`
  匹配字符串的结尾或只是之前换行符结尾的字符串，并在 `多行` 模式下也匹配在换行符之前。`foo`匹配'foo'和'foobar'，而正则表达式`foo$`仅匹配'foo'。更有趣的是，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.
`'*'`
  使得到的RE匹配前面的RE的0个或更多个重复，尽可能多的重复。`ab*`将匹配'a'，'ab'或'a'后跟任意数量的'b'。
`'+'`
  匹配前面重复出现的正则表达式1次或多次，尽可能多的匹配。`ab+`将匹配'a'，后跟不少于一个的'b'；它将不匹配只是'a'。
`'?'`
  使得到的RE匹配前面的RE的0或1个重复。`ab?`将匹配'a'或'ab'。
`*?`，`+?`，`??`
  `'*'`，`'+'`和`'?'`都是 贪婪模式它们尽可能匹配多次。有时我们不希望匹配多次；如果`<.*>`用来匹配 `<a> b <c>`, 它将会匹配所有字符串, 不只是`<a>`.加上`?`限定符将使得匹配为非贪婪模式或者minimal匹配； few尽可能少的字符被匹配。使用`<.*?>`将会仅匹配`<a>`.
`{m}`
  精确的指定RE应该被匹配m次；少于m次将导致RE不会被匹配上。例如, `a{6}`将会精确匹配`'a'`字符6次，五次将不会被匹配。
`{m,n}`
  使得到的RE匹配前面的RE的m到n重复，尝试匹配尽可能多的重复。For example, `a{3,5}` will match from 3 to 5 `'a'` characters. 省略m指定零的下限，省略n指定无限上限。例如，`a{4,}b`将匹配`aaaab`以及1千个`'a'`字符后面跟随一个`b`，但不能匹配`aaab`。逗号不能省略，否则修饰符会与前面描述的形式混淆。
`{m,n}?`
  使得到的RE从前面的RE的m到n重复匹配，尝试尽可能匹配少数重复。这是以前限定符的非贪婪版本。例如，对于6个字符的字符串`'aaaaaa'`，`a{3,5}`将匹配5个`'a'`字符，而 `a{3,5}?`只会匹配3个字符。
`'\'`

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

  如果您没有使用原始字符串来表示模式，请记住Python也使用反斜杠作为字符串文本中的转义序列；如果转义序列不被Python的解析器识别，则反斜杠和后续字符将包含在结果字符串中。但是，如果Python能够识别结果序列，则反斜杠应重复两次。这很复杂，也很难理解，所以强烈建议您使用原始字符串，除了最简单的表达式。

`[]`

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

  - 字符可以被单独罗列，例如：`[amk]` 会匹配 `'a'`, `'m'`, 或 `'k'`.
  - 字符范围可以表明通过给予两个字符和分离他们的 `'-'`、 例如 `[z]` 将匹配任何小写字母的 ASCII 字母、 `[0-5] [0-9]` 将匹配所有两位数数字从 `00` 到 `59`，和 `[0-9A-Fa-f]` 将都匹配任何十六进制数字。如果`-`被转义（例如，`[a\-z]`），或者将它放置为第一个或最后一个字符（例如，`[a-]`），它将匹配文字`'-'`。
  - 在集合内，特殊字符失去特殊意义。例如，`[(+*)]` 将匹配任何字符 `'('`，`'+'`，`'* '`，或 `'')''`。
  - 如`\w` or `\S`等字符类别也是可以被接受的（译者注:不会失去特殊意义），尽管匹配的这些字符取决于`ASCII` or `LOCALE` 模式是否被设置。
  - 不在一个范围内的字符可以通过补充该集合来匹配。如果这个集合的第一个字符是`'^'`, 那么所有不在集合内的将会被匹配上。例如, `[^5]`将会配对除 `'5'`以外的任何字符，并且和`[^^]`将会匹配除`'^'`以外的任何字符。如果`^`不在集合的第一个位置那么它将没有特殊意义。
  - 想要在一个集合内匹配`']'`，需要在它的前面使用一个反斜杠转义，或者在集合开头处将它替换。例如， `[()[\]{}]` and `[]()[{}]` 都将会匹配一对括号。
`'|'`
  `A|B`, where A and B can be arbitrary REs, creates a regular expression that will match either A or 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)` (例如:使用单引号或双引号来匹配一个被引用的字符串):

  引用组的参考内容 | 参考方法
  --- | ---
  在相同的模式本身 | `(?P=quote)` (as shown) `\1`
  处理匹配对象`m`时 | `m.group('quote')` `m.end('quote')` (etc.)
  在传递给`re.sub()`的`repl`参数的字符串中 | `\g<quote>` `\g<1>` `\1`

`(?P=name)`
  对指定组的反向引用；它匹配任何名为name的早期组匹配的文本。
`(?#...)`
  注释：忽略括号内的内容
`(?=...)`
  如果`...`匹配后面的内容则匹配，但不消耗字符串的任何字符。这被称为前瞻性断言。例如，`Isaac (?=Asimov)` 会匹配`'Isaac '`，当且仅当它后面是`'Asimov'`。
`(?!...)`
  如果`...`不匹配后面的内容则匹配。这是一个负前瞻性断言。例如，`Isaac (?!Asimov)`会匹配`'Isaac '`，当且仅当它后面不是`'Asimov'`。
`(?<=...)`

  匹配如果字符串中的当前位置在`...`之前匹配以当前位置结束。这被称为积极lookbehind断言。`(?<=abc)def` will find a match in `abcdef`, since the lookbehind will back up 3 characters and check if the contained pattern matches. 包含的模式只能匹配一些固定长度的字符串，这意味着允许`abc`或`a|b`，但`a*`和`a{3,4}`不是。请注意，以正向lookbehind断言开始的模式在搜索字符串的开头不匹配；你很可能想使用`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中已更改：添加了对固定长度的组引用的支持。

`(?<!...)`
  匹配如果字符串中的当前位置未匹配`...`的匹配项。这被称为否定lookbehind 断言。与正向lookbehind断言类似，所包含的模式只能匹配某些固定长度的字符串。以反向lookbehind断言开头的模式可能会匹配搜索字符串的开头。
`(?(id/name)yes-pattern|no-pattern)`
  如果具有给定 id 或 name 的组存在，将尝试匹配 `yes-pattern`，否则匹配 `no-pattern`。`no-pattern`是可选的，可以省略。例如，`(<)?(\w+@\w+(?:\.\w+)+)(?(1)>|$)` 是一个糟糕的电子邮件匹配模式，这将匹配 `'<user@host.com>'` 以及 `'user@host.com'`, 但不是 `'<user@host.com'` 也不是 `'user@host.com>'`.

特殊序列由`'\'`和下面列表中的字符组成。如果普通字符不在列表中，则所得到的RE将匹配第二个字符。例如，`\$`匹配字符`'$'`。

`\number`
  匹配相对应组编号的内容。组号从1开始。例如，`(.+) \1` 匹配 `'the the'`或者 `'55 55'`, 但是不会匹配`'thethe'` (注意在匹配上的组后有空格)。该特殊序列只能用于匹配前99个组中的一个。如果number的第一个数字为0或number为3个八进制数字，则不会将其解释为组匹配，而是八进制值号 T2>。在字符类的`'['`和`']'`内部，所有数字转义都被视为字符。
`\A`
  仅仅匹配字符串开头
`\b`

  在单词的开始或结束匹配空字符。这里的单词的定义是由Unicode字母数字或下划线组成的序列，因此单词的结束由空格、非字母数字或非下划线Unicode字符表示。注意，`\b`定义为`\w`和`\W`字符直接或`\w`和字符串开始/结束的边界。这意味着，`r'\bfoo\b'`匹配`'foo'`、`'foo.'`、 `'(foo)'`、`'bar foo baz'`，但不能匹配`'foobar'`或`'foo3'`。

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

`\B`
  匹配一个单词不在开头或结尾的字符。这意味着`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字符；这包括大多数可以是任何语言的单词的一部分的字符，以及数字和下划线。If the `ASCII` flag is used, only `[a-zA-Z0-9_]` is matched (but the flag affects the entire regular expression, so in such cases using an explicit `[a-zA-Z0-9_]` may be a better choice).
  对于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'` and `'\U'` escape sequences are only recognized in Unicode patterns. 在字节模式中，它们没有被专门处理。

八进制逃生包括在一个有限的形式。如果第一个数字是0，或者如果有三个八进制数字，则它被认为是八进制转义。否则，它是一个组参考。至于字符串文字，八进制转义字符的长度总是最多三位数字。

Changed in version 3.3: The `'\u'` and `'\U'` escape sequences have been added.

从版本3.5开始弃用，将在版本3.6中删除：由`'\'`和ASCII字母组成的未知转义现在引发了弃用警告，并且在Python 3.6中将被禁止。

也可以看看

掌握正则表达式
  由O'Reilly出版的由Jeffrey Friedl撰写的关于正则表达式的书。本书的第二版不再涵盖Python，但第一版涵盖了编写良好正则表达式模式的细节。

## 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` and `\S` 执行纯 ASCII 匹配，而不是全部Unicode匹配(译注：因为Unicode中存在多国文字页面，所以上述字符存在多个码点，作用相同而二进制不同)。这仅在Unicode模式下有意义，并在 byte 模式下被忽略。

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

`re.``DEBUG`

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

`re.``I`
`re.``IGNORECASE`

  执行不区分大小写的匹配；如`[A-Z]`也会匹配小写字母。这不受当前语言环境的影响，对于Unicode字符能如愿地正常工作。

`re.``L`
`re.``LOCALE`

  Make `\w`，`\W`，`\b`，`\B`，`\s`和`\S`取决于当前的语言环境。由于场所机制非常不可靠，因此不鼓励使用该标志，并且它一次只能处理一种“文化”。您应该使用Unicode匹配代替，这是Unicode 3（Unicode）（str）模式中的默认值。该标志仅在字节模式下才有意义。

  自版本3.5起弃用，将在版本3.6中删除：弃用`re.LOCALE`的字符串模式或`re.ASCII`。

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

  顺序扫描 string ，寻找正则表达式 pattern 产生匹配的第一个位置，并返回相应的 match object。 若string中没有任何位置能匹配该模式，则返回 `None` ；注意：这与在string某处找到一个零宽度匹配不同。

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

  若在 string 起始位置的0个或多个字符匹配正则表达式 pattern，返回相应的 match object。若该 string 不匹配该 pattern ，返回 `None` ； 注意，这与零宽度匹配不同。

  注意，即便在 `MULTILINE` 模式下， `re.match()` 也只会匹配该 string 的开始位置，而不会匹配每一行的开始位置。

  如果要在字符串中的任何位置找到匹配项，请改为使用`search()`（另请参阅search() vs. match()）。

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

  如果整个字符串匹配正则表达式pattern，则返回相应的match object。若该 string 不匹配该 pattern ，返回 `None` ； 注意，这与零宽度匹配不同。

  版本3.4中的新功能。

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

  根据pattern的出现拆分字符串。如果在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()` doesn’t currently split a string on an empty pattern match. 例如：

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

  即使`'x*'`也与'a'之前，'b'和'c'之间以及'c'之后的0'x'匹配，当前这些匹配被忽略。正确的行为（即 splitting on empty matches too and returning `['', 'a', 'b', 'c', '']`) will be implemented in future versions of Python, but since this is a backward incompatible change, a `FutureWarning` will be raised in the meanwhile.

  目前只能匹配空字符串的模式永远不会拆分字符串。由于这与预期的行为不匹配，因此将从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)

  返回字符串中pattern的所有非重叠匹配项作为字符串列表。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)

  在字符串中的RE 模式的所有非重叠匹配中返回产生match objects的iterator。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.``sub`(pattern, repl, string, count=0, flags=0)

  将string中最左侧非重叠出现的pattern替换为repl，返回所获得的字符串。如果未找到该模式，则字符串将保持不变。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'

  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的引用，而不是对组2的引用，后面是文字字符`'0'`。反向引用`\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`

    编译失败的pattern索引。

  `lineno`

    与pos对应的行。

  `colno`

    对应于pos的列。

  在版本3.5中更改：添加了其他属性。

## 6.2.3.正则表达式对象

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

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

  扫描string 寻找正则表达式产生匹配后的第一个位置 , 然后返回一个相对应的 match object.若string中没有任何位置能匹配该模式，则返回 `None` ；注意：这与在string某处找到一个零宽度匹配不同。

  可选的第二个参数 pos 给索引在字符串中搜索在哪里开始;它默认为 `0`。这不完全等同于切分字符串； `'^'`模式字符匹配字符串的实际开始位置和紧跟在换行符后面的位置，但不一定位于搜索开始的索引位置。

  可选参数endpos限制了字符串搜索的距离；它就好像字符串是endpos个字符，所以只有从pos到`endpos 的字符 - t5> 1`将被搜索匹配。If endpos is less than pos, no match will be found; otherwise, if rx is a compiled regular expression object, `rx.search(string, 0, 50)` is equivalent to `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` ，则该字符串不匹配模式;注意这不同于一个零字节长度的匹配。

  可选的pos和endpos参数与`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'>

  如果要在字符串中的任何位置找到匹配项，请改为使用`search()`（另请参阅search() vs. match()）。

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

  如果整个字符串与此正则表达式匹配，则返回相应的match object。若该 string 不匹配该 pattern ，返回 `None` ； 注意，这与零宽度匹配不同。

  可选的pos和endpos参数与`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()` 的功能，使用已编译的模版，但也接受可选的 pos 和 endpos 参数限制搜索区域像 `match （）`。

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

  类似于 `finditer()` 的功能，使用已编译的模版，但也接受可选的 pos 和 endpos 参数像 `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.Match 对象

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

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

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

`match.``expand`(template)

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

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

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

  返回匹配的一个或多个子组。如果有一个参数，结果是一个单一的字符串；如果有多个参数，则结果是每个参数有一个项目的元组。如果没有参数， group1默认为零 （返回整个match的匹配结果）。如果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])

  返回由组匹配的子串的开始和结束的索引； 组默认为零（表示整个匹配的子字符串）。如果组存在但是不参与匹配，则返回`-1`。对于确实有助于匹配的匹配对象m和组g，子组匹配g（等同于`m.group(g)`）是

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

  Note that `m.start(group)` will equal `m.end(group)` if group matched a null string. 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():]
      'tony@tiger.net'

`match.``span`([group])

  对于匹配m，返回2元组`（m.start（group）， m.end（group）） < / T1>。`请注意，如果组没有对匹配做出贡献，则这是`（ - 1， -1）`。组默认为零，整个匹配。

`match.``pos`

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

`match.``endpos`

  传递给regex object的`search()`或`match()`方法的endpos的值。这是RE引擎不会去的字符串的索引。

`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.检查一对

在这个例子中，我们将使用以下辅助函数来更加优雅地显示匹配对象：

    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=()>"

That last hand, `"727ak"`, contained a pair, or two of the same valued cards. 为了与正则表达式匹配，可以使用反向引用：

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

Note however that in `MULTILINE` mode `match()` only matches at the beginning of the string, whereas using `search()` with a regular expression beginning with `'^'` will match at the beginning of each line.

    >>> 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()` splits a string into a list delimited by the passed pattern. 该方法对于将文本数据转换为可由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']]

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

    >>> [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()`用字符串或函数的结果替换每个模式的出现。这个例子演示了如何使用带有函数的`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()` matches all occurrences of a pattern, not just the first one as `search()` does. 例如，如果一个人是作家，并且想要在某些文本中找到所有副词，他或她可以按以下方式使用`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而不是字符串。Continuing with the previous example, if one was a writer who wanted to find all of the adverbs and their positions in some text, he or she would use `finditer()` in the following manner:

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