添加链接
link管理
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

9.7. 模式匹配

PostgreSQL 提供了三种独立的实现模式匹配的方法: SQL LIKE 操作符、更近一些的 SIMILAR TO 操作符(SQL:1999 里添加进来的)和 POSIX -风格的正则表达式。除了这些基本的 "这个串匹配这个模式吗?" 操作符外,还有一些函数可用于提取或替换匹配子串并在匹配位置分离一个串。

提示: 如果你的模式匹配的要求超出了这些,请考虑用 Perl 或 Tcl 写一个用户定义的函数。

小心

当大多数正则表达式搜索可以很快执行时, 正则表达式可以被设计,采取时间和存储过程的任意数量。 警惕接受来自敌对来源的正则表达式搜索模式。 如果你必须这样做,那么建议强加语句超时。

使用 SIMILAR TO 模式的搜索具有相同的安全隐患, 因为 SIMILAR TO 提供了许多诸如 POSIX 风格的规则表达式相同的能力。

LIKE 搜索,比其它两个选项更简单,安全使用可能的敌对模式源。

9.7.1. LIKE

string LIKE pattern [ESCAPE escape-character]
string NOT LIKE pattern [ESCAPE escape-character]

如果该 string 匹配了提供的 pattern ,那么 LIKE 表达式返回真(和预期的一样,如果 LIKE 返回真,那么 NOT LIKE 表达式返回假, 反之亦然。一个等效的表达式是 NOT ( string LIKE pattern ) )。

如果 pattern 不包含百分号或者下划线,那么该模式只代表它本身的串;这时候 LIKE 的行为就象等号操作符。在 pattern 里的下划线 ( _ )代表(匹配)任何单个字符; 而一个百分号( % )匹配任何零或更多个字符的序列。

一些例子:

'abc' LIKE 'abc'    true
'abc' LIKE 'a%'     true
'abc' LIKE '_b_'    true
'abc' LIKE 'c'      false

LIKE 模式匹配总是覆盖整个串。因此,要匹配在串内任何位置的序列,该模式必须以百分号开头和结尾。

要匹配文本的下划线或者百分号,而不是匹配其它字符, 在 pattern 里相应的字符必须 前导逃逸字符。缺省的逃逸字符是反斜线,但是你可以用 ESCAPE 子句指定一个不同的逃逸字符。 要匹配逃逸字符本身,写两个逃逸字符。

注意: 如果你关掉了 standard_conforming_strings ,你在文串常量中写的任何反斜线都需要被双写。详见 第 4.1.2.1 节

请注意反斜线在串文本里已经有特殊含义了,所以如果你写一个 包含反斜线的模式常量,那你就要在 SQL 语句里写两个反斜线。 因此,写一个匹配单个反斜线的模式实际上要在语句里写四个反斜线。 你可以通过用 ESCAPE 选择一个不同的逃逸字符 来避免这样;这样反斜线就不再是 LIKE 的特殊字符了。 但仍然是字符文本分析器的特殊字符,所以你还是需要两个反斜线。) 我们也可以通过写 ESCAPE '' 的方式不选择逃逸字符,这样可以有效地禁用逃逸机制,但是没有办法关闭下划线和百分号在模式中的特殊含义。

关键字 ILIKE 可以用于替换 LIKE , 它令该匹配根据活动区域成为大小写无关。这个不属于 SQL 标准而是一个 PostgreSQL 扩展。

操作符 ~~ 等效于 LIKE , 而 ~~* 对应 ILIKE 。 还有 !~~ !~~* 操作符分别代表 NOT LIKE NOT ILIKE 。所有这些操作符都是 PostgreSQL 特有的。

9.7.2. SIMILAR TO 正则表达式

string SIMILAR TO pattern [ESCAPE escape-character]
string NOT SIMILAR TO pattern [ESCAPE escape-character]

SIMILAR TO 操作符根据自己的模式是否匹配给定串而返回真或者假。 它和 LIKE 非常类似,只不过它使用 SQL 标准定义的正则表达式理解模式。 SQL 正则表达式是在 LIKE 标记和普通的正则表达式标记的奇怪的杂交。

类似 LIKE SIMILAR TO 操作符只有在它的模式匹配整个串的时候才能成功;这一点和普通的 正则表达式的行为不同,在普通的正则表达式里,模式匹配串的任意部分。 和 LIKE 类似的地方还有, SIMILAR TO 使用 _ % 作为分别代表任意单个字符和任意串的通配符(这些可以比得上 POSIX 正则表达式里的 . .* )。

除了这些从 LIKE 借用的功能之外, SIMILAR TO 支持下面这些从 POSIX 正则表达式借用的 模式匹配元字符:

  • | 表示选择(两个候选之一)。

  • * 表示重复前面的项零次或更多次。

  • + 表示重复前面的项一次或更多次。

  • ? 表示重复前面的项零次或一次。

  • { m } 表示重复前面的项刚好 m 次。

  • { m ,} 表示重复前面的项 m 次或更多次。

  • { m , n } 表示重复前面的项至少 m 次并且不超过 n 次。

  • 可以使用圆括号 () 把多个项组合成一个逻辑项。

  • 一个方括号表达式 [...] 声明一个字符类,就像 POSIX 正则表达式一样。 注意点号( . )不是 SIMILAR TO 的一个元字符。

    LIKE 一样,反斜线禁用所有这些元字符的特殊含义;当然我们也可以用 ESCAPE 指定一个不同的逃逸字符。

    一些例子:

    'abc' SIMILAR TO 'abc'      true
    'abc' SIMILAR TO 'a'        false
    'abc' SIMILAR TO '%(b|d)%'  true
    'abc' SIMILAR TO '(b|c)%'   false

    带三个参数的 substring ,即 substring( string from pattern for escape-character ) ,提供了抽取一个匹配 SQL 正则表达式的子串的方法。和 SIMILAR TO 一样,声明的模式必须匹配整个数据串,否则函数失败并返回空值。为了标识在成功的时候应该返回的模式部分,模式 必须包含逃逸字符的两次出现,并且后面要跟上双引号( " )。匹配这两个标记之间的模式的文本将被返回。

    一些例子,使用 #" 定界返回串:

    substring('foobar' from '%#"o_b#"%' for '#')   oob
    substring('foobar' from '#"o_b#"%' for '#')    NULL

9.7.3. POSIX 正则表达式

表 9-14 列出了所有可用于 POSIX 正则表达式模式匹配的操作符。

POSIX 正则表达式提供了比 LIKE SIMILAR TO 操作符更强大的含义。许多 Unix 工具,例如 egrep sed awk 使用一种与我们这里描述的类似的模式匹配语言。

正则表达式是一个字符序列,它是定义一个串集合 (一个 正则集 )的缩写。 如果一个串是正则表达式描述的正则集中的一员时, 我们就说这个串匹配该正则表达式。 和 LIKE 一样,模式字符准确地匹配串字符, 除非在正则表达式语言里有特殊字符 — 不过正则表达式用的 特殊字符和 LIKE 用的不同。 和 LIKE 模式不一样的是,正则表达式允许匹配串里的任何位置,除非该正则表达式显式地挂接在串的开头或者结尾。

一些例子:

'abc' ~ 'abc'    true
'abc' ~ '^a'     true
'abc' ~ '(b|d)'  true
'abc' ~ '^(b|c)' false

POSIX 模式语言的详细描述见下文。

带两个参数的 substring 函数,即 substring( string from pattern ) ,提供了抽取一个匹配 POSIX 正则表达式模式的子串的方法。如果没有匹配它返回空值,否则就是文本中匹配模式的那部分。 但是如果该模式包含任何圆括号,那么将返回匹配第一对子表达式(对应第一个左圆括号的) 的文本。如果你想在表达式里使用圆括号而又不想导致这个例外,那么你可以在整个表达式外边放上一对圆括号。 如果你需要在想抽取的子表达式前有圆括号,参阅后文描述的非捕获性圆括号。

一些例子:

substring('foobar' from 'o.b')     oob
substring('foobar' from 'o(.)b')   o

regexp_replace 函数提供了将匹配 POSIX 正则表达式模式的子串替换为新文本的功能。 它的语法是 regexp_replace ( source , pattern , replacement [ , flags ])。 如果没有匹配 pattern ,那么返回不加修改的 source 串。 如果有匹配,则返回的 source 串里面的匹配子串将被 replacement 串替换掉。 replacement 串可以包含 \ n , 其中 \ n 是 1 到 9, 表明源串里匹配模式里第 n 个圆括号子表达式的子串应该被插入, 并且它可以包含 \& 表示应该插入匹配整个模式的子串。如果你需要放一个文字形式的反斜线在替换文本里,那么写 \\ flags 参数是一个可选的文本串,它包含另个或更多单字母标志,这些标志可以改变函数的行为。标志 i 指定大小写无关的匹配,而标志 g 指定替换每一个匹配的子串而不仅仅是第一个。支持的标志(但不是 g )在 表 9-22 中描述。

一些例子:

regexp_replace('foobarbaz', 'b..', 'X')
                                   fooXbaz
regexp_replace('foobarbaz', 'b..', 'X', 'g')
                                   fooXX
regexp_replace('foobarbaz', 'b(..)', E'X\\1Y', 'g')
                                   fooXarYXazY

regexp_matches 函数返回一个文本数组,该数组由匹配一个 POSIX 正则表达式模式得到的所有被捕获子串构成。其语法是 regexp_matches ( string , pattern [ , flags ])。该函数可以不返回任何行、返回一行或者返回多行(见下文的 g )。如果 pattern 不匹配,该函数不返回行。如果模式不包含圆括号子表达式,则每一个被返回的行都是一个单一元素的文本数组,其中包括匹配整个模式的子串。如果模式包含圆括号子表达式,该函数返回一个文本数组,它的第 n 个元素是匹配模式的第 n 个圆括号子表达式的子串( "非捕获" 圆括号不计算在内,详见下文)。 flags 参数是一个可选的文本字符串,它包含零个或更多个单字母标志,它们可以改变函数的行为。标志 g 让函数寻找串中的每一个匹配,而不仅仅是第一个,并且为每一个这样的匹配返回一行。支持的标志(但不是 g )在 表 9-22 中描述。

一些例子:

SELECT regexp_matches('foobarbequebaz', '(bar)(beque)');
 regexp_matches 
----------------
 {bar,beque}
(1 row)
SELECT regexp_matches('foobarbequebazilbarfbonk', '(b[^b]+)(b[^b]+)', 'g');
 regexp_matches 
----------------
 {bar,beque}
 {bazil,barf}
(2 rows)
SELECT regexp_matches('foobarbequebaz', 'barbeque');
 regexp_matches 
----------------
 {barbeque}
(1 row)

也可以强制 regexp_matches() 通过使用一个子选择来总是返回一行。当你希望所有行都被返回(甚至是不能匹配的行)时,把它用在一个 SELECT 目标列表中会特别有用:

SELECT col1, (SELECT regexp_matches(col2, '(bar)(beque)')) FROM tab;

regexp_split_to_table 把一个 POSIX 正则表达式模式当作一个定界符来分离一个串。它的语法形式是 regexp_split_to_table ( string , pattern [ , flags ])。如果没有与 pattern 的匹配,该函数返回 string 。如果有至少有一个匹配,对每一个匹配它都返回从上一个匹配的末尾(或者串的开头)到这次匹配开头之间的文本。当没有更多匹配时,它返回从上一次匹配的末尾到串末尾之间的文本。 flags 参数是一个可选的文本串,它包含零个或更多单字母标志,这些标识可以改变该函数的行为。 regexp_split_to_table 能支持的标志在 表 9-22 中描述。

regexp_split_to_array 函数的行为和 regexp_split_to_table 相同,不过 regexp_split_to_array 会把它的结果以一个 text 数组的形式返回。它的语法是 regexp_split_to_array ( string , pattern [ , flags ])。这些参数和 regexp_split_to_table 的相同。

一些例子:

SELECT foo FROM regexp_split_to_table('the quick brown fox jumps over the lazy dog', E'\\s+') AS foo;
-------
 quick  
 brown  
 jumps 
(9 rows)
SELECT regexp_split_to_array('the quick brown fox jumps over the lazy dog', E'\\s+');
              regexp_split_to_array             
-----------------------------------------------
 {the,quick,brown,fox,jumps,over,the,lazy,dog}
(1 row)
SELECT foo FROM regexp_split_to_table('the quick brown fox', E'\\s*') AS foo;
-----
(16 rows)

正如上一个例子所示,正则表达式分离函数会忽略零长度的匹配,这种匹配发生在串的开头或结尾或者正好发生在前一个匹配之后。这和正则表达式匹配的严格定义是相悖的,后者由 regexp_matches 实现,但是通常前者是实际中最常用的行为。其他软件系统如Perl也使用相似的定义。

9.7.3.1. 正则表达式细节

PostgreSQL 的正则表达式是使用 Henry Spencer 写的一个包来实现的。下面的正则表达式的大部分描述都是从他的手册页中逐字拷贝过来的。

正则表达式( RE ),在 POSIX 1003.2 中定义, 它有两种形式: 扩展 RE 或者是 ERE (大概地说就是那些在 egrep 里的), 基本 RE 或者是 BRE (大概地说就是那些在 ed 里的)。 PostgreSQL 支持两种形式,并且还实现了一些POSIX标准中没有但是在类似 Perl 或者 Tcl 这样的语言中得到广泛应用的一些扩展。使用了那些非POSIX扩展的 RE 高级 RE , 或者本文档里说的 ARE 。ARE 几乎完全是 ERE 的超集,但是 BRE 有几个符号上的不兼容(以及更多的限制)。我们首先描述 ARE 和 ERE 形式, 描述那些只适用于 ARE 的特性,然后描述 BRE 的区别是什么。

注意: PostgreSQL 初始时总是推测一个正则表达式遵循 ARE 规则。但是,可以通过为 RE 模式预置一个 embedded option 来选择限制更多的 ERE 或 BRE 规则,如 第 9.7.3.4 节 中所述。这对为期望准确的 POSIX 1003.2 规则的应用提供兼容性很有用。

一个正则表达式被定义为一个或更多 分支 ,它们之间被 | 分隔。只要能匹配其中一个分支的东西都能匹配正则表达式。

一个分支是一个或多个 量化原子 或者 约束 连接而成。一个原子匹配第一个,然后后面的原子匹配第二个, 以此类推;一个空分支匹配空串。

一个量化原子是一个 原子 , 后面可能跟着一个 量词 。没有量词的时候,它匹配一个原子, 有量词的时候,它可以匹配若干个原子。一个 原子 可以是在 表 9-15 里面显示的任何可能。 可能的量词和它们的含义在 表 9-16 里显示。

一个 约束 匹配一个空串,但只是在满足特定条件下才匹配。 约束可以在能够使用原子的地方使用,只是它不能跟着量词。简单的约束在 表 9-17 里显示; 更多的约束稍后描述。

RE 不能以反斜线( \ )结尾。

注意: 如果你关掉了 standard_conforming_strings ,任何你写在文字串常量中的反斜线都需要被双写。详见 第 4.1.2.1 节

使用 { ... } 的形式被称作 范围 。 一个范围内的数字 m n 都是无符号十进制整数, 允许的数值从 0 到 255(包含)。

非贪婪 的量词(只在 ARE 中可用)匹配对应的正常 ( 贪婪 )模式,区别是它寻找最少的匹配,而不是最多的匹配。详见 第 9.7.3.5 节

注意: 一个量词不能紧跟在另外一个量词后面,例如 ** 是非法的。量词不能作为表达式或者子表达式的开头,也不能跟在 ^ 或者 | 后面。

Lookahead和lookbehind约束不能包含 后引用 (参阅 第 9.7.3.3 节 ), 并且其中的所有圆括号都被认为是非捕获的。

9.7.3.2. 方括号表达式

方括号表达式 是一个包围在 [] 中的字符列表。它通常匹配列表中的任意单个字符(但见下文)。 如果列表以 ^ 开头,它匹配任意单个 在该列表参与部分中的字符。如果该列表中两个字符用 - 隔开, 那它就是那两个字符(包括在内)之间的所有字符范围的缩写,例如,在 ASCII [0-9] 匹配任何十进制数字。两个范围共享一个端点是非法的,例如, a-c-e 。范围与字符集关系密切, 可移植的程序应该避免依靠它们。

想在列表中包含文本 ] ,可以让它做列表的首字符(如果使用了 ^ ,需要放在其后)。 想在列表中包含文本 - ,可以让它做列表的首字符或者尾字符,或者一个范围的第二个端点。 想在列表中把文本 - 当做范围的起点, 把它用 [. .] 包围起来,这样它就成为一个排序元素(见下文)。 除了这些字符本身、一些用 [ 的组合(见下段)以及逃逸(只在 ARE 中有效)以外,所有其它特殊字符 在方括号表达式里都失去它们的特殊含义。特别是,在 ERE 和 BRE 规则下 \ 不是特殊的, 但在 ARE 里,它是特殊的(引入一个逃逸)。

在一个方括号表达式里,一个排序元素(一个字符、一个被当做一个单一字符排序的多字符序列或者一个表示上面两种情况的排序序列名称) 包含在 [. .] 里面的时候表示该排序元素的字符序列。该序列被当做该方括号列表 的一个单一元素。这允许一个包含多字符排序元素的方括号表达式去匹配多于一个字符,例如,如果排序序列包含一个 ch 排序元素, 那么 RE [[.ch.]]*c 匹配 chchcc 的头五个字符。

注意: PostgreSQL 当前不支持多字符排序元素。这些信息描述了将来可能有的行为。

在方括号表达式里,包围在 [= =] 里的排序元素是一个 等价类 , 代表等效于那一个的所有排序元素的字符序列,包括它本身(如果没有其它等效排序元素,那么就好象封装定界符是 [. .] )。例如,如果 o ^ 是一个等价类的成员,那么 [[=o=]] [[=^=]] [o^] 都是同义的。一个等价类不能是一个范围的端点。

在方括号表达式里,在 [: :] 里面封装的字符类的名字代表属于该类的所有字符的列表。 标准的字符类名字是: alnum alpha blank cntrl digit graph lower print punct space upper xdigit 。 它们代表在 ctype 中定义的字符类。 一个区域可以会提供其他的类。字符类不能用做一个范围的端点。

方括号表达式里有两个特例:方括号表达式 [[:<:]] [[:>:]] 是约束,分别匹配一个单词开头和结束的空串。 单词定义为一个单词字符序列,前面和后面都没有其它单词字符。单词字符是一个 alnum 字符(和 ctype 中定义的一样) 或者一个下划线。这是一个扩展,兼容 POSIX 1003.2, 但那里面并没有说明, 而且在准备移植到其他系统里去的软件里一定要小心使用。通常下文描述的约束逃逸更好些(它们并非更标准,但是更容易键入)。

9.7.3.3. 正则表达式逃逸

逃逸 是以 \ 开头,后面跟着一个字母数字字符得特殊序列。 逃逸有好几种变体:字符项、类缩写、约束逃逸以及后引用。在 ARE 里, 如果一个 \ 后面跟着一个字母数字,但是并未组成一个合法的逃逸, 那么它是非法的。在 ERE 中没有逃逸:在方括号表达式之外,一个后面跟着字母数字字符的 \ 只是表示该字符是一个普通的字符,而且在一个方括号表达式里, \ 是一个普通的字符(后者实际上在 ERE 和 ARE 不兼容)。

字符项逃逸 用于便于我们在 RE 中声明那些不可打印的或其他习惯的字符。它们显示在 表 9-18 中。

类缩写逃逸 用来提供一些常用的字符类缩写。它们显示在 表 9-19 中。

约束逃逸 是一个约束,如果满足特定的条件,它匹配该空串。它们显示在 表 9-20 中。

后引用 \ n )匹配数字 \ n 指定的被前面的圆括号子表达式匹配的同一个串 (参阅 表 9-21 )。例如, ([bc])\1 匹配 bb 或者 cc , 但是不匹配 bc 或者 cb 。RE 中子表达式必须完全在后引用前面。子表达式以它们的先导圆括号的顺序编号。非捕获圆括号并不定义子表达式。

十六进制位是 0 - 9 a - f A - F 。八进制位是 0 - 7

指定ASCII范围以外的值(0-127)数字字符输入逃逸依赖于数据库编码。 当编码是UTF-8的时候,逃避值相当于Unicode代码点, 例如 \u1234 即字符 U+1234 。 对于其它多字节编码,字符输入转义通常只指定字符的字节值的连接。 如果逃逸值不与数据库编码中的任何合法字符相对应,不会抛出错误, 但它永远不会匹配任何数据。

字符项逃逸总是被当作普通字符。例如, \135 是 ASCII 中的 ] , 但 \135 并不终止一个方括号表达式。

在方括号表达式里, \d \s \w 会失去它们的外层方括号,而 \D \S \W 是非法的(也就是说,例如 [a-c\d] 等效于 [a-c[:digit:]] 。同样 [a-c\D] 等效于 [a-c^[:digit:]] 的,也是非法的)。

一个词被定义成在上面 [[:<:]] [[:>:]] 中的声明。在方括号表达式里,约束逃逸是非法的。

9.7.3.4. 正则表达式元语法

除了上面描述的主要语法之外,还有几种特殊形式和杂项语法。

如果一个 RE 以 ***: 开头,那么剩下的 RE 都被当作 ARE(这在 PostgreSQL 中通常是无效的,因为 RE 被假定为 ARE,但是如果 ERE 或 BRE 模式通过 flags 参数被指定为一个正则表达式函数时,它确实能产生效果)。如果一个 RE 以 ***= 开头, 那么剩下的 RE 被当作一个文本串,所有的字符都被认为是一个普通字符。

一个 ARE 可以以 嵌入选项 开头:一个序列 (? xyz ) (这里的 xyz 是一个或多个字母字符)声明影响剩余 RE 的选项。 这些选项覆盖任何前面判断的选项 — 特别地,它们可以覆盖一个正则表达式操作符隐含的大小写敏感的行为,或者覆盖 flags 参数中的正则表达式函数。可用的选项字母在 表 9-22 中显示。注意这些同样的选项字母也被用在正则表达式函数的 flags 参数中。

嵌入选项在 ) 终止序列时发生作用。它们只在 ARE 的开始处起作用 (在任何可能存在的 ***: 控制器后面)。

除了通常的( )RE 语法(这种情况下所有字符都有效), 还有一种 扩展 语法,可以通过声明嵌入的 x 选项获得。在扩展语法里,RE 中的空白字符被忽略,就像那些在 # 和其后的新行(或 RE 的末尾)之间的字符一样。这样就允许我们给一个复杂的 RE 分段和注释。不过这个基本规则有三种例外:

9.7.3.5. 正则表达式匹配规则

在 RE 可以在给定串中匹配多于一个子串的情况下, RE 匹配串中最靠前的那个子串。如果 RE 可以匹配在那个位置开始 的多个子串,要么是取最长的子串,要么是最短的,具体哪种, 取决于 RE 是 贪婪 的还是 非贪婪 的。

一个 RE 是否贪婪取决于下面规则:

  • 大多数原子以及所有约束,都没有贪婪属性(因为它们毕竟无法匹配个数变化的文本)。

  • 在一个 RE 周围加上圆括号并不会改变其贪婪性。

  • 带一个固定重复次数量词 ( { m } 或者 { m }? ) 的量化原子和原子自身具有同样的贪婪性(可能是没有)。

  • 一个带其他普通的量词(包括 { m , n } m 等于 n 的情况)的量化原子是贪婪的(首选最长匹配)。

  • 一个带非贪婪量词(包括 { m , n }? m 等于 n 的情况)的量化原子是非贪婪的(首选最短匹配)。

  • 一个分支 — 也就是说,一个没有顶级 | 操作符的 RE — 和它里面的第一个有贪婪属性的量化原子有着同样的贪婪性。

  • 一个由 | 操作符连接起来的两个或者更多分支组成的 RE 总是贪婪的。

    上面的规则所描述的贪婪属性不仅仅适用于独立的量化原子, 而且也适用于包含量化原子的分支和整个 RE。这里的意思是, 匹配是按照分支或者整个 RE 作为一个整体 匹配最长或者最短的可能子串。 一旦整个匹配的长度确定,那么匹配任意特定子表达式的部分就基于该子表达式的贪婪属性进行判断,在 RE 里面靠前的子表达式的优先级高于靠后的子表达式。

    一个相应的例子:

    SELECT SUBSTRING('XY1234Z', 'Y*([0-9]{1,3})');
    结果:123
    SELECT SUBSTRING('XY1234Z', 'Y*?([0-9]{1,3})');
    结果:1

    在第一个例子里,RE 作为整体是贪婪的,因为 Y* 是贪婪的。它可以匹配从 Y 开始的东西,并且它匹配从这个位置开始的最长的串, 也就是, Y123 。输出是这里的圆括号包围的部分,或者说是 123 。在第二个例子里, RE 总体上是一个非贪婪的 RE,因为 Y*? 是非贪婪的。它可以匹配从 Y 开始的最短的子串,也就是说 Y1 。子表达式 [0-9]{1,3} 是贪婪的,但是它不能修改总体匹配长度的决定; 因此它被迫只匹配 1

    简而言之,如果一个 RE 同时包含贪婪和非贪婪的子表达式,那么总的匹配长度要么是尽可能长,要么是尽可能短,这取决于给整个 RE 赋予的属性。给子表达式赋予的属性只影响在这个匹配里,各个子表达式之间相互允许 "吃掉" 的多少。

    量词 {1,1} {1,1}? 可以分别用于在一个子表达式或者整个 RE 上强制 贪婪或者非贪婪。 当你需要整个RE具有不同于从它的元素推导出的贪婪属性,这是非常有用的。 作为一个例子, 假设我们正在尝试分隔包含一些数字到它们之前和之后的数字和部分的字符串。 我们可能会尝试这样做:

    SELECT regexp_matches('abc01234xyz', '(.*)(\d+)(.*)');
    Result: {abc0123,4,xyz}

    这不工作:第一个 .* 是贪婪的,因此它可以尽可能多的 "吃" , 剩下的 \d+ 在最后可能的位置上匹配最后一个数字。 我们可能会通过使它不贪婪尝试修复:

    SELECT regexp_matches('abc01234xyz', '(.*?)(\d+)(.*)');
    Result: {abc,0,""}

    这也不能工作,因为现在RE作为一个整体是非贪婪的, 并且它尽快结束了整体匹配。 我们可以得到我们想要通过迫使RE作为一个整体是贪婪的东西:

    SELECT regexp_matches('abc01234xyz', '(?:(.*?)(\d+)(.*)){1,1}');
    Result: {abc,01234,xyz}

    控制从组件贪婪中分离出RE整体贪婪允许处理可变长度的模式更加灵活。

    当决定什么是较长或较短匹配的时候, 匹配长度是以字符衡量的,而不是排序元素。一个空串会被认为比什么都不匹配长。例如: bb* 匹配 abbbc 的中间三个字符; (week|wee)(night|knights) 匹配 weeknights 的所有十个字符; 而 (.*).* 匹配 abc 的时候,圆括号包围的子表达式匹配所有三个字符;当 (a*)* 被拿来匹配 bc 时,整个 RE 和圆括号 子表达式都匹配一个空串。

    如果声明了大小写无关的匹配,那么效果就好像所有大小写区别在字母表中消失了。如果在多个情况中一个字母以一个普通字符的形式出现在方括号表达式外面,那么它实际上被转换成 一个包含大小写的方括号表达式,也就是说,x 变成 [xX]。 如果它出现在一个方括号表达式里面,那么它的所有大小写的同族都被加入 方括号表达式中,也就是说, x 变成 [xX] 。当它出现在一个方括号表达式内时,它的所有大小写副本都被加入到方括号表达式中,例如, [x] 会变成 [xX] ,而 [^x] 会变成 [^xX]

    如果指定了新行敏感的匹配, . 和使用 ^ 的方括号表达式 将永远不会匹配新行字符(这样,匹配就绝对不会跨越新行,除非 RE 显式地安排了这样的情况)并且 ^ $ 除了分别匹配串开头和结尾之外,还将分别匹配新行后面和前面的空串。但是 ARE 逃逸 \A \Z 仍然 匹配串的开头和结尾。

    如果指定了部分新行敏感的匹配,那么它影响 . 和方括号表达式, 这个时候和新行敏感的匹配一样,但是不影响 ^ $

    如果指定了逆新行敏感匹配,那么它影响 ^ $ ,其作用和在新行敏感的匹配里一样,但是不影响 . 和方括号表达式。这个并不是很有用,只是为了满足对称性而提供的。

9.7.3.6. 限制和兼容性

在这个实现里,对 RE 的长度没有特别的限制。但是,那些希望高移植性的程序应该避免使用长度超过 256 字节的 RE,因为 POSIX 兼容 的实现可以拒绝接受这样的 RE。

ARE 实际上和 POSIX ERE 不兼容的唯一的特性是在方括号表达式里 \ 并不失去它特殊的含义。所有其它 ARE 特性都使用在 POSIX ERE 里面是非法或者是未定义、未声明效果的语法;指示器的 *** 就是在 POSIX 的 BRE 和 ERE 之外的语法。

许多 ARE 扩展都是从 Perl 那里借来的(但是有些被做了修改来清理它们),以及一些 Perl 里没有出现的扩展。要注意的不兼容性包括 \b \B 、对结尾的新行缺乏特别的处理、对那些被新行敏感匹配的东西附加的补齐方括号表达式、在 lookahead 约束里对圆括号和后引用的限制以及最长/最短 匹配(而不是第一匹配)的语义。

PostgreSQL 7.4 之前的版本中识别的 ARE 和 ERE 语法存在两个非常明显的不兼容:

  • 在 ARE 中,后面跟着一个字母数字字符的 \ 要么是一个逃逸要么是一个错误, 但是在以前的版本里,它只是写该字母数字字符的另外一种方法。这个应该不是什么问题, 因为在以前的版本里没有什么理由会让我们写这样的序列。

  • 在 ARE 里, \ [] 里还是一个特殊字符, 因此在方括号表达式里的一个文本 \ 必须被写成 \\

9.7.3.7. 基本正则表达式

BRE 在几个方面和 ERE 不太一样。在 BRE 中, | + ? 都是普通字符并且没有与它们功能等价的东西。范围的定界符是 \{ \} , 因为 { } 本身是普通字符。嵌套的子表达式的圆括号是 \( \) ,因为 ( ) 自身是普通字符。除非在 RE 开头或者是圆括号子表达式开头, ^ 都是一个普通字符。 除非在 RE 结尾或者是圆括号子表达式的结尾, $ 是一个普通字符。如果 * 出现在 RE 开头或者是圆括号封装的子表达式开头 (前面可能有 ^ ),那么它是个普通字符。最后,可以用单数字的后引用, \< \> 分别是 [[:<:]] [[:>:]] 的同义词;在 BRE 中没有其它可用的逃逸。