Python 正则表达式模块使用 BRE 还是 ERE?
Does the Python regular expression module use BRE or ERE?
看来 POSIX 将正则表达式的实现分为两种:基本正则表达式 (BRE) 和扩展正则表达式 (ERE)。
Python re
module参考好像没有说明。
除了语法上的一些相似性,re
模块 不遵循 POSIX 正则表达式标准 .
不同的匹配语义
POSIX 正则表达式(可以用 DFA/NFA 甚至回溯引擎实现)总是找到 最左边的最长匹配 ,而 re
模块是一个回溯引擎,它找到最左边的"earliest"匹配("earliest"根据正则表达式定义的搜索顺序)。
在匹配 (Prefix|PrefixSuffix)
与 PrefixSuffix
的情况下可以观察到匹配语义的差异。
- 在 POSIX-complaint 实现 POSIX 正则表达式(不是那些只借用语法的)中,正则表达式将匹配
PrefixSuffix
.
- 相比之下,
re
引擎(以及许多其他回溯正则表达式引擎)将仅匹配 Prefix
,因为 Prefix
在交替中首先指定。
在匹配 (xxx|xxxxx)*
与 xxxxxxxxxx
的情况下也可以看出差异(10 个 x
的字符串):
在 Cygwin 上:
$ [[ "xxxxxxxxxx" =~ (xxx|xxxxx)* ]] && echo "${BASH_REMATCH[0]}"
xxxxxxxxxx
全部 10 x
个匹配。
在Python:
>>> re.search(r'(?:xxx|xxxxx)*', 'xxxxxxxxxxx').group(0)
'xxxxxxxxx'
只有 9 个 x
匹配,因为它在所有 3 次重复中交替选择第一个项目 xxx
,并且没有任何强制它回溯并交替尝试第二个项目)
POSIX-独家正则表达式功能
除了匹配语义的不同,POSIX正则表达式还定义了比较符号、等价class表达式的语法,以及 基于排序规则的字符范围 。这些特性大大增加了正则表达式的表达能力。
以等价class表达式为例,来自文档:
An equivalence class expression shall represent the set of collating elements belonging to an equivalence class, as described in Collation Order. [...]. The class shall be expressed by enclosing any one of the collating elements in the equivalence class within bracket-equal ( "[="
and "=]"
) delimiters. For example, if 'a'
, 'à'
, and 'â'
belong to the same equivalence class, then "[[=a=]b]"
, "[[=à=]b]"
, and "[[=â=]b]"
are each equivalent to "[aàâb]"
. [...]
由于这些功能在很大程度上取决于语言环境设置,因此相同的正则表达式在不同的语言环境中可能表现不同。它还取决于系统上的区域设置数据的整理顺序。
re
正则表达式特征
re
借鉴了 Perl 的语法,但并非 Perl 正则表达式中的所有功能都在 re
中实现。以下是 re
中可用的一些正则表达式功能,这些功能在 POSIX 正则表达式中不可用:
Greedy/lazy量词,指定展开量词的顺序。
虽然人们通常称POSIX中的*
是贪婪的,但它实际上只是规定了POSIX中重复的下界和上界。所谓"greedy"行为是由于最左最长匹配规则。
- 环视断言(前瞻和后视)
- 条件模式
(?(id/name)yes-pattern|no-pattern)
- 速记结构:
\b
、\s
、\d
、\w
(某些 POSIX 正则表达式引擎可能会实现这些,因为标准对于这些情况,行为未定义)
都没有。它基本上是 PCRE 方言,但有不同的实现。
re
文档中的第一句话说:
This module provides regular expression matching operations similar to those found in Perl.
虽然这不会立即向新来者揭示他们与例如POSIX 正则表达式,众所周知,Perl 4 和后来的 Perl 5 提供了比早期工具的正则表达式功能大幅扩展的功能集,包括 POSIX 为 grep -E
授权的又名 ERE .
perlre
manual page 更详细地描述了正则表达式的功能,尽管您会在 Python 文档中以不同的形式找到很多相同的详细信息。 Perl 手册页包含这段历史:
The patterns used in Perl pattern matching evolved from those supplied in the Version 8 regex routines. (The routines are derived (distantly) from Henry Spencer's freely redistributable reimplementation of the V8 routines.)
(这里V8指的是Version 8 Unix. Spencer's library basically (re)implemented POSIX regular expressions。)
Perl 4 有大量方便的结构,如 \d
、\s
、\w
以及符号速记,如 \t
、\f
、 \n
。 Perl 5 添加了一组重要的扩展(仍在缓慢增长),包括但不限于
- 非贪婪量词
- 非回溯量词
- Unicode 符号和 属性 支持
- 非分组括号
- 先行和后行
- ...基本上任何以
(?
开头的东西
因此,"regular" 表达式不再是严格的 "regular"。
这由 Philip Hazell 在便携式库中重新实现,最初用于 Exim 邮件服务器;他的 PCRE library 已经进入无数不同的应用程序,包括许多编程语言(Ruby、PHP、Python 等)。顺便说一句,尽管名称如此,但该库并不是严格意义上的 "Perl compatible"(不再);在功能和行为上都有差异。 (例如,Perl 在内部将 *
更改为 {0,32767}
之类的东西,而 PCRE 会做其他事情。)
Python 的早期版本实际上有一个不同的正则表达式实现,并且有 plans to change it again(尽管它基本上仍然是 PCRE)。这是 Python 2.7 / 3.5 的情况。
看来 POSIX 将正则表达式的实现分为两种:基本正则表达式 (BRE) 和扩展正则表达式 (ERE)。
Python re
module参考好像没有说明。
除了语法上的一些相似性,re
模块 不遵循 POSIX 正则表达式标准 .
不同的匹配语义
POSIX 正则表达式(可以用 DFA/NFA 甚至回溯引擎实现)总是找到 最左边的最长匹配 ,而 re
模块是一个回溯引擎,它找到最左边的"earliest"匹配("earliest"根据正则表达式定义的搜索顺序)。
在匹配 (Prefix|PrefixSuffix)
与 PrefixSuffix
的情况下可以观察到匹配语义的差异。
- 在 POSIX-complaint 实现 POSIX 正则表达式(不是那些只借用语法的)中,正则表达式将匹配
PrefixSuffix
. - 相比之下,
re
引擎(以及许多其他回溯正则表达式引擎)将仅匹配Prefix
,因为Prefix
在交替中首先指定。
在匹配 (xxx|xxxxx)*
与 xxxxxxxxxx
的情况下也可以看出差异(10 个 x
的字符串):
在 Cygwin 上:
$ [[ "xxxxxxxxxx" =~ (xxx|xxxxx)* ]] && echo "${BASH_REMATCH[0]}" xxxxxxxxxx
全部 10
x
个匹配。在Python:
>>> re.search(r'(?:xxx|xxxxx)*', 'xxxxxxxxxxx').group(0) 'xxxxxxxxx'
只有 9 个
x
匹配,因为它在所有 3 次重复中交替选择第一个项目xxx
,并且没有任何强制它回溯并交替尝试第二个项目)
POSIX-独家正则表达式功能
除了匹配语义的不同,POSIX正则表达式还定义了比较符号、等价class表达式的语法,以及 基于排序规则的字符范围 。这些特性大大增加了正则表达式的表达能力。
以等价class表达式为例,来自文档:
An equivalence class expression shall represent the set of collating elements belonging to an equivalence class, as described in Collation Order. [...]. The class shall be expressed by enclosing any one of the collating elements in the equivalence class within bracket-equal (
"[="
and"=]"
) delimiters. For example, if'a'
,'à'
, and'â'
belong to the same equivalence class, then"[[=a=]b]"
,"[[=à=]b]"
, and"[[=â=]b]"
are each equivalent to"[aàâb]"
. [...]
由于这些功能在很大程度上取决于语言环境设置,因此相同的正则表达式在不同的语言环境中可能表现不同。它还取决于系统上的区域设置数据的整理顺序。
re
正则表达式特征
re
借鉴了 Perl 的语法,但并非 Perl 正则表达式中的所有功能都在 re
中实现。以下是 re
中可用的一些正则表达式功能,这些功能在 POSIX 正则表达式中不可用:
Greedy/lazy量词,指定展开量词的顺序。
虽然人们通常称POSIX中的
*
是贪婪的,但它实际上只是规定了POSIX中重复的下界和上界。所谓"greedy"行为是由于最左最长匹配规则。- 环视断言(前瞻和后视)
- 条件模式
(?(id/name)yes-pattern|no-pattern)
- 速记结构:
\b
、\s
、\d
、\w
(某些 POSIX 正则表达式引擎可能会实现这些,因为标准对于这些情况,行为未定义)
都没有。它基本上是 PCRE 方言,但有不同的实现。
re
文档中的第一句话说:
This module provides regular expression matching operations similar to those found in Perl.
虽然这不会立即向新来者揭示他们与例如POSIX 正则表达式,众所周知,Perl 4 和后来的 Perl 5 提供了比早期工具的正则表达式功能大幅扩展的功能集,包括 POSIX 为 grep -E
授权的又名 ERE .
perlre
manual page 更详细地描述了正则表达式的功能,尽管您会在 Python 文档中以不同的形式找到很多相同的详细信息。 Perl 手册页包含这段历史:
The patterns used in Perl pattern matching evolved from those supplied in the Version 8 regex routines. (The routines are derived (distantly) from Henry Spencer's freely redistributable reimplementation of the V8 routines.)
(这里V8指的是Version 8 Unix. Spencer's library basically (re)implemented POSIX regular expressions。)
Perl 4 有大量方便的结构,如 \d
、\s
、\w
以及符号速记,如 \t
、\f
、 \n
。 Perl 5 添加了一组重要的扩展(仍在缓慢增长),包括但不限于
- 非贪婪量词
- 非回溯量词
- Unicode 符号和 属性 支持
- 非分组括号
- 先行和后行
- ...基本上任何以
(?
开头的东西
因此,"regular" 表达式不再是严格的 "regular"。
这由 Philip Hazell 在便携式库中重新实现,最初用于 Exim 邮件服务器;他的 PCRE library 已经进入无数不同的应用程序,包括许多编程语言(Ruby、PHP、Python 等)。顺便说一句,尽管名称如此,但该库并不是严格意义上的 "Perl compatible"(不再);在功能和行为上都有差异。 (例如,Perl 在内部将 *
更改为 {0,32767}
之类的东西,而 PCRE 会做其他事情。)
Python 的早期版本实际上有一个不同的正则表达式实现,并且有 plans to change it again(尽管它基本上仍然是 PCRE)。这是 Python 2.7 / 3.5 的情况。