2024 年 9 月 26 日: PostgreSQL 17 发布!
支持的版本:当前 (17) / 16 / 15 / 14 / 13 / 12
开发版本:devel
不受支持的版本:11 / 10 / 9.6 / 9.5 / 9.4 / 9.3 / 9.2 / 9.1 / 9.0 / 8.4 / 8.3 / 8.2 / 8.1 / 8.0 / 7.4 / 7.3 / 7.2 / 7.1

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]

LIKE 表达式如果 字符串 匹配提供的 模式,则返回 true。(正如预期的那样,NOT LIKE 表达式如果 LIKE 返回 true,则返回 false,反之亦然。等效表达式为 NOT (字符串 LIKE 模式)。)

如果 模式 不包含百分号或下划线,那么该模式只代表字符串本身;在这种情况下,LIKE 的作用与等于运算符相同。模式 中的下划线 (_) 代表(匹配)任何单个字符;百分号 (%) 匹配任何零个或多个字符的序列。

一些示例

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

LIKE 模式匹配始终覆盖整个字符串。因此,如果需要匹配字符串中的任何序列,则该模式必须以百分号开头和结尾。

要匹配文字下划线或百分号而不匹配其他字符,模式 中的相应字符必须以转义字符开头。默认转义字符是反斜杠,但可以使用 ESCAPE 子句选择不同的转义字符。要匹配转义字符本身,请写入两个转义字符。

注意

如果您启用了 standard_conforming_strings,则在文字字符串常量中编写的任何反斜杠都需要加倍。有关更多信息,请参见 第 4.1.2.1 节

也可以通过编写 ESCAPE '' 来选择不使用转义字符。这实际上禁用了转义机制,这使得在模式中无法关闭下划线和百分号的特殊含义。

根据 SQL 标准,省略 ESCAPE 意味着没有转义字符(而不是默认为反斜杠),并且不允许使用零长度的 ESCAPE 值。PostgreSQL 在这方面略微偏离标准。

关键词 ILIKE 可用于代替 LIKE,以根据活动区域设置进行不区分大小写的匹配。这不在SQL标准中,但它是 PostgreSQL 的扩展。

运算符 ~~ 等效于 LIKE,而 ~~* 对应于 ILIKE。还有 !~~!~~* 运算符分别代表 NOT LIKENOT ILIKE。所有这些运算符都是 PostgreSQL 特定的。您可能会在 EXPLAIN 输出和类似位置看到这些运算符名称,因为解析器实际上将 LIKE 等转换为这些运算符。

短语 LIKEILIKENOT LIKENOT ILIKE 通常在 PostgreSQL 语法中被视为运算符;例如,它们可以在 表达式 运算符 ANY (子查询) 结构中使用,尽管不能在那里包含 ESCAPE 子句。在某些情况下,可能需要使用底层运算符名称来代替。

另请参见以开头匹配的运算符 ^@ 以及相应的 starts_with() 函数,这些函数在只需要匹配字符串开头的用例中很有用。

9.7.2. SIMILAR TO 正则表达式 #

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

SIMILAR TO 运算符根据其模式是否匹配给定字符串来返回 true 或 false。它类似于 LIKE,除了它使用 SQL 标准中定义的正则表达式来解释模式。SQL 正则表达式是 LIKE 符号和常用(POSIX)正则表达式符号之间的一种奇怪混合体。

LIKE 一样,SIMILAR TO 运算符只有在其模式匹配整个字符串时才成功;这与常用正则表达式行为不同,在常用正则表达式行为中,模式可以匹配字符串的任何部分。与 LIKE 一样,SIMILAR TO 使用 _% 作为通配符,分别表示任何单个字符和任何字符串(这类似于 POSIX 正则表达式中的 ..*)。

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

  • | 表示交替(两个备选方案中的一个)。

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

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

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

  • {m} 表示前面一项重复正好 m 次。

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

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

  • 圆括号 () 可用于将项目分组为单个逻辑项目。

  • 方括号表达式 [...] 指定一个字符类,就像在 POSIX 正则表达式中一样。

请注意,句点 (.) 对于 SIMILAR TO 来说不是元字符。

LIKE 一样,反斜杠会禁用这些元字符的特殊含义。可以使用 ESCAPE 指定不同的转义字符,或者通过编写 ESCAPE '' 来禁用转义功能。

根据 SQL 标准,省略 ESCAPE 意味着没有转义字符(而不是默认为反斜杠),并且不允许使用零长度的 ESCAPE 值。PostgreSQL 在这方面略微偏离标准。

另一个非标准扩展是,在转义字符后面加上字母或数字可以访问为 POSIX 正则表达式定义的转义序列;请参见下面的 表 9.20表 9.21表 9.22

一些示例

'abc' SIMILAR TO 'abc'          true
'abc' SIMILAR TO 'a'            false
'abc' SIMILAR TO '%(b|d)%'      true
'abc' SIMILAR TO '(b|c)%'       false
'-abc-' SIMILAR TO '%\mabc\M%'  true
'xabcy' SIMILAR TO '%\mabc\M%'  false

具有三个参数的 substring 函数提供提取匹配 SQL 正则表达式模式的子字符串的功能。该函数可以根据标准 SQL 语法编写

substring(string similar pattern escape escape-character)

或者使用现在已过时的 SQL:1999 语法

substring(string from pattern for escape-character)

或者作为普通的三个参数函数

substring(string, pattern, escape-character)

SIMILAR TO 相似,指定的模式必须与整个数据字符串匹配,否则函数将失败并返回 null。为了指示匹配数据子字符串感兴趣的模式部分,模式应包含两个转义字符,后跟双引号 (")。当匹配成功时,返回匹配这些分隔符之间模式部分的文本。

转义双引号分隔符实际上将 substring 的模式划分为三个独立的正则表达式;例如,三个部分中的任何一个中的竖线 (|) 仅影响该部分。此外,当存在有关数据字符串的哪一部分与哪个模式匹配的歧义时,这三个正则表达式的第一个和第三个被定义为匹配尽可能少的文本,而不是尽可能多的文本。(用 POSIX 的术语来说,第一个和第三个正则表达式被强制为非贪婪的。)

作为对 SQL 标准的扩展,PostgreSQL 允许只使用一个转义双引号分隔符,在这种情况下,第三个正则表达式被视为为空;或者没有分隔符,在这种情况下,第一个和第三个正则表达式被视为为空。

以下是一些示例,使用 #" 分隔返回字符串

substring('foobar' similar '%#"o_b#"%' escape '#')   oob
substring('foobar' similar '#"o_b#"%' escape '#')    NULL

9.7.3. POSIX正则表达式 #

表 9.16 列出了使用 POSIX 正则表达式的模式匹配可用的运算符。

表 9.16. 正则表达式匹配运算符

运算符

描述

示例

text ~ textboolean

字符串与正则表达式匹配,区分大小写

'thomas' ~ 't.*ma't

text ~* textboolean

字符串与正则表达式匹配,不区分大小写

'thomas' ~* 'T.*ma't

text !~ textboolean

字符串与正则表达式不匹配,区分大小写

'thomas' !~ 't.*max't

text !~* textboolean

字符串与正则表达式不匹配,不区分大小写

'thomas' !~* 'T.*ma'f


POSIX正则表达式提供了一种比 LIKESIMILAR TO 运算符更强大的模式匹配方法。许多 Unix 工具(如 egrepsedawk)使用与这里描述的类似的模式匹配语言。

正则表达式是一个字符序列,它是字符串集(正则集)的简短定义。如果一个字符串是正则表达式所描述的正则集的成员,则称该字符串与正则表达式匹配。与 LIKE 相似,模式字符与字符串字符完全匹配,除非它们是正则表达式语言中的特殊字符——但正则表达式使用与 LIKE 不同的特殊字符。与 LIKE 模式不同,正则表达式允许匹配字符串中的任何位置,除非正则表达式明确地锚定到字符串的开头或结尾。

一些示例

'abcd' ~ 'bc'     true
'abcd' ~ 'a.c'    true — dot matches any character
'abcd' ~ 'a.*d'   true — * repeats the preceding pattern item
'abcd' ~ '(b|x)'  true — | means OR, parentheses group
'abcd' ~ '^a'     true — ^ anchors to start of string
'abcd' ~ '^(b|c)' false — would match except for anchoring

POSIX模式语言将在下面更详细地介绍。

具有两个参数的 substring 函数,substring(string from pattern),提供提取与 POSIX 正则表达式模式匹配的子字符串。如果不存在匹配项,它将返回 null,否则返回与模式匹配的文本的第一部分。但如果模式包含任何括号,则返回与第一个带括号的子表达式(其左括号在最前面)匹配的文本部分。如果要在不触发此异常的情况下在表达式中使用括号,可以将整个表达式括在括号中。如果需要在要提取的子表达式之前在模式中使用括号,请参见下面描述的非捕获括号。

一些示例

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

函数 regexp_count 计算 POSIX 正则表达式模式与字符串匹配的位置数量。它的语法为 regexp_count(string, pattern [, start [, flags ]])。通常从字符串的开头在 string 中搜索 pattern,但如果提供了 start 参数,则从该字符索引开始。参数 flags 是一个可选的文本字符串,包含零个或多个更改函数行为的单字母标记。例如,在 flags 中包含 i 表示不区分大小写匹配。支持的标记在 表 9.24 中进行了描述。

一些示例

regexp_count('ABCABCAXYaxy', 'A.')          3
regexp_count('ABCABCAXYaxy', 'A.', 1, 'i')  4

函数 regexp_instr 返回 POSIX 正则表达式模式与字符串的第 N 次匹配的起始或结束位置,如果不存在此类匹配,则返回零。它的语法为 regexp_instr(string, pattern [, start [, N [, endoption [, flags [, subexpr ]]]]])。通常从字符串的开头在 string 中搜索 pattern,但如果提供了 start 参数,则从该字符索引开始。如果指定了 N,则定位模式的第 N 次匹配,否则定位第一次匹配。如果省略了参数 endoption 或将其指定为零,则函数返回匹配项的第一个字符的位置。否则,endoption 必须为 1,函数返回匹配项后一个字符的位置。参数 flags 是一个可选的文本字符串,包含零个或多个更改函数行为的单字母标记。支持的标记在 表 9.24 中进行了描述。对于包含带括号的子表达式的模式,subexpr 是一个整数,表示感兴趣的子表达式:结果标识与该子表达式匹配的子字符串的位置。子表达式按其起始括号的顺序编号。当省略 subexpr 或其值为零时,结果标识整个匹配的位置,而不考虑带括号的子表达式。

一些示例

regexp_instr('number of your street, town zip, FR', '[^,]+', 1, 2)
                                   23
regexp_instr('ABCDEFGHI', '(c..)(...)', 1, 1, 0, 'i', 2)
                                   6

函数 regexp_like 检查 POSIX 正则表达式模式的匹配是否出现在字符串中,返回布尔值 true 或 false。它的语法为 regexp_like(string, pattern [, flags ]). 参数 flags 是一个可选的文本字符串,包含零个或多个更改函数行为的单字母标记。支持的标记在 表 9.24 中进行了描述。如果未指定任何标记,则此函数与 ~ 运算符具有相同的结果。如果仅指定了 i 标记,则它与 ~* 运算符具有相同的结果。

一些示例

regexp_like('Hello World', 'world')       false
regexp_like('Hello World', 'world', 'i')  true

函数 regexp_match 返回一个文本数组,其中包含 POSIX 正则表达式模式与字符串的第一次匹配中的匹配子字符串。它的语法为 regexp_match(string, pattern [, flags ]). 如果不存在匹配项,则结果为 NULL。如果找到匹配项,并且 pattern 不包含带括号的子表达式,则结果是一个包含与整个模式匹配的子字符串的单元素文本数组。如果找到匹配项,并且 pattern 包含带括号的子表达式,则结果是一个文本数组,其第 n 个元素是与 pattern 的第 n 个带括号的子表达式匹配的子字符串(不包括 非捕获 括号;有关详细信息,请参见下文)。参数 flags 是一个可选的文本字符串,包含零个或多个更改函数行为的单字母标记。支持的标记在 表 9.24 中进行了描述。

一些示例

SELECT regexp_match('foobarbequebaz', 'bar.*que');
 regexp_match
--------------
 {barbeque}
(1 row)

SELECT regexp_match('foobarbequebaz', '(bar)(beque)');
 regexp_match
--------------
 {bar,beque}
(1 row)

提示

在您只想要整个匹配的子字符串或在没有匹配时想要 NULL 的常见情况下,最佳解决方案是使用 regexp_substr()。但是,regexp_substr() 仅存在于 PostgreSQL 版本 15 及更高版本中。在较旧的版本中工作时,可以提取 regexp_match() 结果的第一个元素,例如

SELECT (regexp_match('foobarbequebaz', 'bar.*que'))[1];
 regexp_match
--------------
 barbeque
(1 row)

函数 regexp_matches 返回一个文本数组集,其中包含 POSIX 正则表达式模式与字符串匹配中的匹配子字符串。它的语法与 regexp_match 相同。如果不存在匹配项,则此函数不返回任何行;如果存在匹配项并且未给出 g 标记,则返回一行;或者如果存在 N 个匹配项并且给出了 g 标记,则返回 N 行。每行返回的都是一个文本数组,包含整个匹配的子字符串或与 pattern 的带括号的子表达式匹配的子字符串,与上面针对 regexp_match 的描述相同。regexp_matches 接受 表 9.24 中显示的所有标记,以及 g 标记,该标记命令它返回所有匹配项,而不仅仅是第一个匹配项。

一些示例

SELECT regexp_matches('foo', 'not there');
 regexp_matches
----------------
(0 rows)

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

提示

在大多数情况下,应将 regexp_matches()g 标记一起使用,因为如果您只需要第一个匹配项,使用 regexp_match() 更加容易和高效。但是,regexp_match() 仅存在于 PostgreSQL 版本 10 及更高版本中。在较旧的版本中工作时,一个常见的技巧是将 regexp_matches() 调用放在子查询中,例如

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

如果存在匹配项,这将生成一个文本数组,否则生成 NULL,与 regexp_match() 的行为相同。如果没有子查询,此查询将不会为没有匹配项的表行生成任何输出,这通常不是期望的行为。

函数 regexp_replace 提供了使用 POSIX 正则表达式模式匹配子字符串并将其替换为新文本的功能。其语法为 regexp_replace(source, pattern, replacement [, start [, N ]] [, flags ])。 (请注意,除非指定了 start,否则无法指定 N,但可以在任何情况下指定 flags)。如果 pattern 没有匹配,则 source 字符串将保持不变。如果匹配,则 source 字符串将返回,并将匹配的子字符串替换为 replacement 字符串。 replacement 字符串可以包含 \n,其中 n 是 1 到 9,表示应插入与模式的第 n 个带括号的子表达式匹配的源子字符串,并且可以包含 \&,表示应插入与整个模式匹配的子字符串。如果您需要在替换文本中添加一个字面反斜杠,请编写 \\。 通常情况下,从字符串的开头开始在 string 中搜索 pattern,但如果提供了 start 参数,则从该字符索引开始。默认情况下,只替换模式的第一个匹配项。如果指定了 N 并且大于零,则替换模式的第 N 个匹配项。如果给出了 g 标志,或者如果指定了 N 并且为零,则替换 start 位置或之后的所有匹配项。(当指定 N 时,g 标志将被忽略)。 flags 参数是一个可选的文本字符串,其中包含零个或多个单字母标志,这些标志会改变函数的行为。支持的标志 (尽管不是 g) 在 表 9.24 中描述。

一些示例

regexp_replace('foobarbaz', 'b..', 'X')
                                   fooXbaz
regexp_replace('foobarbaz', 'b..', 'X', 'g')
                                   fooXX
regexp_replace('foobarbaz', 'b(..)', 'X\1Y', 'g')
                                   fooXarYXazY
regexp_replace('A PostgreSQL function', 'a|e|i|o|u', 'X', 1, 0, 'i')
                                   X PXstgrXSQL fXnctXXn
regexp_replace('A PostgreSQL function', 'a|e|i|o|u', 'X', 1, 3, 'i')
                                   A PostgrXSQL function

函数 regexp_split_to_table 使用 POSIX 正则表达式模式作为分隔符来分割字符串。其语法为 regexp_split_to_table(string, pattern [, flags ])。如果 pattern 没有匹配,则该函数返回 string。如果至少有一个匹配,则对于每个匹配,它将返回从上次匹配的末尾 (或字符串的开头) 到匹配的开头的文本。当没有更多匹配项时,它将返回从上次匹配的末尾到字符串末尾的文本。 flags 参数是一个可选的文本字符串,其中包含零个或多个单字母标志,这些标志会改变函数的行为。 regexp_split_to_table 支持 表 9.24 中描述的标志。

函数 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', '\s+') AS foo;
  foo
-------
 the
 quick
 brown
 fox
 jumps
 over
 the
 lazy
 dog
(9 rows)

SELECT regexp_split_to_array('the quick brown fox jumps over the lazy dog', '\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', '\s*') AS foo;
 foo
-----
 t
 h
 e
 q
 u
 i
 c
 k
 b
 r
 o
 w
 n
 f
 o
 x
(16 rows)

如最后一个示例所示,regexp 分割函数会忽略在字符串开头或结尾或紧接在上次匹配之后出现的零长度匹配。这与其他 regexp 函数实现的 regexp 匹配的严格定义相反,但在实践中通常是最方便的行为。其他软件系统 (如 Perl) 使用类似的定义。

函数 regexp_substr 返回与 POSIX 正则表达式模式匹配的子字符串,如果未匹配,则返回 NULL。其语法为 regexp_substr(string, pattern [, start [, N [, flags [, subexpr ]]]]). 通常情况下,从字符串的开头开始在 string 中搜索 pattern,但如果提供了 start 参数,则从该字符索引开始。如果指定了 N,则返回模式的第 N 个匹配项,否则返回第一个匹配项。 flags 参数是一个可选的文本字符串,其中包含零个或多个单字母标志,这些标志会改变函数的行为。支持的标志在 表 9.24 中描述。对于包含带括号的子表达式的模式,subexpr 是一个整数,表示感兴趣的子表达式:结果是与该子表达式匹配的子字符串。子表达式按其前导括号的顺序编号。当省略或 subexpr 为零时,无论带括号的子表达式如何,结果都是整个匹配项。

一些示例

regexp_substr('number of your street, town zip, FR', '[^,]+', 1, 2)
                                    town zip
regexp_substr('ABCDEFGHI', '(c..)(...)', 1, 1, 'i', 2)
                                   FGH

9.7.3.1. 正则表达式详细信息 #

PostgreSQL 的正则表达式使用 Henry Spencer 编写的软件包实现。下面的正则表达式的描述大部分是从他的手册中逐字复制的。

正则表达式 (REs),如POSIX1003.2 中所定义,有两种形式:扩展REs 或EREs (大约相当于 egrep),以及 基本REs 或BREs (大约相当于 ed)。 PostgreSQL 支持这两种形式,并且还实现了一些不在 POSIX 标准中但由于在 Perl 和 Tcl 等编程语言中可用而被广泛使用的扩展。REs 使用这些非 POSIX 扩展被称为 高级REs 或AREs 在本文档中。ARE 几乎是 ERE 的一个精确超集,但 BRE 有几个表示法上的不兼容性 (以及更加有限)。我们首先描述 ARE 和 ERE 形式,并注意到仅适用于 ARE 的特性,然后描述 BRE 的不同之处。

注意

PostgreSQL 始终最初假定正则表达式遵循 ARE 规则。但是,可以通过在 RE 模式前面添加一个 嵌入式选项来选择更有限的 ERE 或 BRE 规则,如 第 9.7.3.4 节 中所述。这对于与期望严格遵循POSIX1003.2 规则的应用程序兼容非常有用。

正则表达式定义为一个或多个 分支,由 | 分隔。它匹配与其中一个分支匹配的任何内容。

一个分支是零个或多个 量化的原子约束,连接在一起。它匹配第一个的匹配项,接着是第二个的匹配项,等等;空分支匹配空字符串。

一个量化的原子是一个 原子,后面可能跟一个 量词。如果没有量词,则它匹配原子的匹配项。使用量词,它可以匹配原子的一些匹配项。一个 原子 可以是 表 9.17 中显示的任何可能性。可能的量词及其含义显示在 表 9.18 中。

一个 约束 匹配空字符串,但仅在满足特定条件时才匹配。约束可以在可以使用原子的地方使用,但它不能后面跟着量词。简单的约束显示在 表 9.19 中;一些更复杂的约束将在后面介绍。

表 9.17. 正则表达式原子

原子 描述
(re) (其中 re 是任何正则表达式) 匹配 re 的匹配项,并注意该匹配项以备报告
(?:re) 如上,但不注意匹配项以备报告 (一个 非捕获 括号组) (仅适用于 ARE)
. 匹配任何单个字符
[chars] 一个 括号表达式,匹配任何一个 chars (有关详细信息,请参见 第 9.7.3.2 节)
\k (其中 k 是一个非字母数字字符) 匹配作为普通字符的该字符,例如,\\ 匹配一个反斜杠字符
\c 其中 c 是字母数字 (可能后面跟着其他字符) 是一个 转义,请参见 第 9.7.3.3 节 (仅适用于 ARE;在 ERE 和 BRE 中,这匹配 c)
{ 当后面跟着一个非数字字符时,匹配左大括号字符 {;当后面跟着一个数字时,它是 bound (见下文) 的开头
x 其中 x 是一个没有其他意义的单个字符,匹配该字符

RE 不会以反斜杠 (\) 结尾。

注意

如果您启用了 standard_conforming_strings,则在文字字符串常量中编写的任何反斜杠都需要加倍。有关更多信息,请参见 第 4.1.2.1 节

表 9.18. 正则表达式量词

量词 匹配
* 0 个或多个原子的匹配序列
+ 1 个或多个原子的匹配序列
? 0 个或 1 个原子的匹配序列
{m} 正好 m 个原子的匹配序列
{m,} m 个或更多个原子的匹配序列
{m,n} mn (含) 个原子的匹配序列;m 不能超过 n
*? * 的非贪婪版本
+? + 的非贪婪版本
?? ? 的非贪婪版本
{m}? {m} 的非贪婪版本
{m,}? {m,} 的非贪婪版本
{m,n}? {m,n} 的非贪婪版本

使用 {...} 的形式称为 边界。边界内的数字 mn 是无符号十进制整数,允许的值从 0 到 255(包括 0 和 255)。

非贪婪 量词(仅在 ARE 中可用)匹配与其对应的普通(贪婪)量词相同的可能性,但更倾向于匹配最少次数而不是最多次数。有关更多详细信息,请参见 第 9.7.3.5 节

注意

量词不能紧跟另一个量词,例如,** 无效。量词不能在表达式或子表达式开头,也不能紧跟 ^|

表 9.19。正则表达式约束

约束 描述
^ 匹配字符串开头
$ 匹配字符串结尾
(?=re) 正向预查 在匹配 re 的子字符串开始的任何位置匹配(仅限 ARE)。
(?!re) 负向预查 在不匹配 re 的子字符串开始的任何位置匹配(仅限 ARE)。
(?<=re) 正向回顾 在匹配 re 的子字符串结束的任何位置匹配(仅限 ARE)。
(?<!re) 负向回顾 在不匹配 re 的子字符串结束的任何位置匹配(仅限 ARE)。

预查和回顾约束不能包含 反向引用(请参见 第 9.7.3.3 节),它们内部的所有括号都被认为是非捕获的。

9.7.3.2. 方括号表达式 #

方括号表达式 是一个用 [] 括起来的字符列表。它通常匹配列表中的任何单个字符(但请参见下文)。如果列表以 ^ 开头,则它匹配列表中 包含的任何单个字符。如果列表中的两个字符用 - 分隔,则它是对这两个字符之间(包括这两个字符)在排序顺序中的所有字符范围的简写,例如,[0-9]ASCII中匹配任何十进制数字。两个范围共享一个端点是非法的,例如,a-c-e。范围非常依赖于排序顺序,因此便携式程序应避免依赖它们。

要在列表中包含一个字面 ],将其作为第一个字符(如果使用,则在 ^ 之后)。要在列表中包含一个字面 -,将其作为第一个或最后一个字符,或作为范围的第二个端点。要使用一个字面 - 作为范围的第一个端点,将其用 [..] 括起来,使其成为排序元素(请参见下文)。除了这些字符之外,一些使用 [(请参见下一段)和转义(仅限 ARE)的组合,所有其他特殊字符在方括号表达式内都失去了其特殊意义。特别是,\ 在遵循 ERE 或 BRE 规则时不是特殊的,尽管在 ARE 中它是特殊的(作为引入转义)。

在方括号表达式中,用 [..] 括起来的排序元素(一个字符、一个排序为单个字符的多个字符序列或任一种的排序顺序名称)代表该排序元素的字符序列。该序列被视为方括号表达式列表中的单个元素。这允许包含多个字符排序元素的方括号表达式匹配多个字符,例如,如果排序顺序包括 ch 排序元素,那么 RE [[.ch.]]*c 将匹配 chchcc 的前五个字符。

注意

PostgreSQL 目前不支持多字符排序元素。此信息描述了可能的未来行为。

在方括号表达式中,用 [==] 括起来的排序元素是一个 等价类,代表所有与该排序元素等价的排序元素的字符序列,包括它本身。(如果没有其他等价排序元素,则处理方式与将括起来的定界符视为 [..] 相同。)例如,如果 o^ 是等价类的成员,那么 [[=o=]][[=^=]][o^] 都是同义词。等价类不能是范围的端点。

在方括号表达式中,用 [::] 括起来的字符类名称代表属于该类的所有字符的列表。字符类不能用作范围的端点。该POSIX标准定义了以下字符类名称:alnum(字母和数字)、alpha(字母)、blank(空格和制表符)、cntrl(控制字符)、digit(数字)、graph(除空格之外的可打印字符)、lower(小写字母)、print(包括空格在内的可打印字符)、punct(标点符号)、space(任何空白)、upper(大写字母)和 xdigit(十六进制数字)。这些标准字符类的行为在 7 位 ASCII 集合中的字符上通常在平台之间是一致的。给定非 ASCII 字符是否被认为属于这些类之一取决于用于正则表达式函数或运算符的 排序规则(请参见 第 23.2 节),或者默认情况下取决于数据库的 LC_CTYPE 本地设置(请参见 第 23.1 节)。非 ASCII 字符的分类在平台之间可能有所不同,即使在名称相似的本地设置中也是如此。(但是 C 本地设置从不认为任何非 ASCII 字符属于这些类。)除了这些标准字符类之外,PostgreSQL 还定义了 word 字符类,它与 alnum 相同,还包括下划线 (_) 字符,以及 ascii 字符类,它包含完整的 7 位 ASCII 集合。

方括号表达式有两种特殊情况:方括号表达式 [[:<:]][[:>:]] 是约束,分别匹配单词开头和结尾的空字符串。单词定义为不以单词字符开头也不以单词字符结尾的单词字符序列。单词字符是属于 word 字符类的任何字符,即任何字母、数字或下划线。这是对POSIX1003.2 的扩展,与它兼容但不属于它的规范,在打算移植到其他系统的软件中应谨慎使用。下面描述的约束转义通常更可取;它们不比标准更标准,但更容易输入。

9.7.3.3. 正则表达式转义 #

转义 是以 \ 开头,后跟一个字母数字字符的特殊序列。转义有几种类型:字符输入、类简写、约束转义和反向引用。ARE 中的 \ 后面跟着一个字母数字字符,但不是有效的转义,是非法的。在 ERE 中,没有转义:在方括号表达式之外,\ 后面跟着一个字母数字字符仅代表该字符作为普通字符,而在方括号表达式内,\ 是一个普通字符。(后者是 ERE 和 ARE 之间的实际不兼容之处。)

字符输入转义 使在 RE 中指定非打印字符和其他不便字符变得更加容易。它们如 表 9.20 所示。

类简写转义 为某些常用的字符类提供简写。它们如 表 9.21 所示。

约束转义 是一个约束,如果满足特定条件,则匹配空字符串,写成转义形式。它们如 表 9.22 所示。

反向引用 (\n) 匹配由数字 n 指定的先前括号子表达式匹配的相同字符串(请参见 表 9.23)。例如,([bc])\1 匹配 bbcc,但不匹配 bccb。子表达式必须完全位于 RE 中的反向引用之前。子表达式按其前导括号的顺序编号。非捕获括号不定义子表达式。反向引用仅考虑由引用子表达式匹配的字符串字符,不考虑它包含的任何约束。例如,(^\d)\1 将匹配 22

表 9.20。正则表达式字符输入转义

转义 描述
\a 警报(铃声)字符,如 C 中一样
\b 退格键,如 C 中一样
\B 反斜杠 (\) 的同义词,有助于减少反斜杠加倍的需要
\cX (其中 X 是任何字符)低位 5 位与 X 相同,其他位全为零的字符
\e 排序顺序名称为 ESC 的字符,或者如果没有,则八进制值为 033 的字符
\f 换页符,如 C 中一样
\n 换行符,如 C 中一样
\r 回车键,如 C 中一样
\t 水平制表符,如 C 中一样
\uwxyz (其中 wxyz 恰好是四个十六进制数字)十六进制值为 0xwxyz 的字符
\Ustuvwxyz (其中 stuvwxyz 恰好是八个十六进制数字)十六进制值为 0xstuvwxyz 的字符
\v 垂直制表符,如 C 中一样
\xhhh (其中 hhh 是任何十六进制数字序列)十六进制值为 0xhhh 的字符(无论使用多少个十六进制数字,都是单个字符)
\0 值为 0 的字符(空字节)
\xy (其中 xy 恰好是两个八进制数字,并且不是 反向引用)八进制值为 0xy 的字符
\xyz (其中 xyz 恰好是三个八进制数字,并且不是 反向引用) 则表示八进制值为 0xyz 的字符。

十六进制数字是 0-9a-f 以及 A-F。八进制数字是 0-7

指定 ASCII 范围之外的值 (0-127) 的数字字符输入转义符的含义取决于数据库编码。当编码为 UTF-8 时,转义值等同于 Unicode 代码点,例如 \u1234 表示字符 U+1234。对于其他多字节编码,字符输入转义符通常仅指定字符的字节值的串联。如果转义值与数据库编码中任何合法字符不对应,则不会引发错误,但它永远不会匹配任何数据。

字符输入转义符始终被视为普通字符。例如,\135 在 ASCII 中是 ],但 \135 不会终止方括号表达式。

表 9.21. 正则表达式类速记转义符

转义 描述
\d 匹配任何数字,如 [[:digit:]]
\s 匹配任何空白字符,如 [[:space:]]
\w 匹配任何单词字符,如 [[:word:]]
\D 匹配任何非数字,如 [^[:digit:]]
\S 匹配任何非空白字符,如 [^[:space:]]
\W 匹配任何非单词字符,如 [^[:word:]]

类速记转义符也可以在方括号表达式中使用,尽管上面显示的定义在该上下文中语法上并不完全有效。例如,[a-c\d] 等同于 [a-c[:digit:]]

表 9.22. 正则表达式约束转义符

转义 描述
\A 仅在字符串开头匹配 (有关它与 ^ 的区别,请参见 第 9.7.3.5 节)
\m 仅在单词开头匹配
\M 仅在单词结尾匹配
\y 仅在单词开头或结尾匹配
\Y 仅在不是单词开头或结尾的位置匹配
\Z 仅在字符串结尾匹配 (有关它与 $ 的区别,请参见 第 9.7.3.5 节)

单词的定义与上面 [[:<:]][[:>:]] 的规范相同。约束转义符在方括号表达式中是非法的。

表 9.23. 正则表达式反向引用

转义 描述
\m (其中 m 是一个非零数字) 对第 m 个子表达式的反向引用
\mnn (其中 m 是一个非零数字,nn 是几个数字,十进制值 mnn 不大于到目前为止看到的闭合捕获圆括号的数量) 对第 mnn 个子表达式的反向引用

注意

八进制字符输入转义符和反向引用之间存在固有的歧义,这可以通过以下启发式方法解决,如上所述。前导零始终表示八进制转义。单个非零数字,后面没有其他数字,始终被视为反向引用。如果一个多位数字序列没有以零开头,并且它位于合适的子表达式之后(即该数字在反向引用的合法范围内),则将其视为反向引用,否则将其视为八进制。

9.7.3.4. 正则表达式元语法 #

除了上面描述的主要语法之外,还提供了一些特殊形式和杂项语法设施。

一个 RE 可以以两种特殊的 指导 前缀之一开头。如果一个 RE 以 ***: 开头,则 RE 的其余部分被视为 ARE。(这在 PostgreSQL 中通常没有效果,因为 RE 被认为是 ARE;但如果 ERE 或 BRE 模式已由正则表达式函数的 flags 参数指定,则会产生效果。)如果一个 RE 以 ***= 开头,则 RE 的其余部分被视为一个文字字符串,其中所有字符都被视为普通字符。

一个 ARE 可以以 嵌入式选项 开头:一个序列 (?xyz) (其中 xyz 是一个或多个字母字符) 指定影响 RE 剩余部分的选项。这些选项会覆盖任何先前确定的选项——特别是,它们可以覆盖正则表达式运算符隐含的区分大小写行为,或正则表达式函数的 flags 参数。可用的选项字母如 表 9.24 所示。请注意,这些相同的选项字母在正则表达式函数的 flags 参数中使用。

表 9.24. ARE 嵌入式选项字母

选项 描述
b RE 的剩余部分是一个 BRE
c 区分大小写匹配 (覆盖运算符类型)
e RE 的剩余部分是一个 ERE
i 不区分大小写匹配 (请参见 第 9.7.3.5 节) (覆盖运算符类型)
m n 的历史同义词
n 区分换行符匹配 (请参见 第 9.7.3.5 节)
p 部分区分换行符匹配 (请参见 第 9.7.3.5 节)
q RE 的剩余部分是一个文字 (引用) 字符串,所有普通字符
s 不区分换行符匹配 (默认)
t 严格语法 (默认;请参见下文)
w 反向部分区分换行符 (怪异) 匹配 (请参见 第 9.7.3.5 节)
x 扩展语法 (请参见下文)

嵌入式选项在 ) 结束序列时生效。它们只能出现在 ARE 的开头 (如果有,则在 ***: 指令之后)。

除了通常的 (严格) RE 语法(其中所有字符都有意义)之外,还有一种 扩展 语法,可以通过指定嵌入式 x 选项来获得。在扩展语法中,RE 中的空白字符将被忽略,RE 中 # 和下一个换行符(或 RE 的结尾)之间的所有字符也将被忽略。这允许对复杂的 RE 进行分段和注释。该基本规则有三个例外

  • \ 开头的空白字符或 # 将被保留

  • 方括号表达式中的空白字符或 # 将被保留

  • 空白字符和注释不能出现在多字符符号中,例如 (?:

为此目的,空白字符是空格、制表符、换行符以及属于 space 字符类的任何字符。

最后,在 ARE 中,在方括号表达式之外,序列 (?#ttt) (其中 ttt 是任何不包含 ) 的文本) 是一种注释,完全被忽略。同样,这在多字符符号的字符之间是不允许的,例如 (?:。此类注释更像是历史遗留物,而不是有用的设施,因此不建议使用它们;请改用扩展语法。

如果初始 ***= 指令指定用户输入应被视为文字字符串而不是 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})');
Result: 123
SELECT SUBSTRING('XY1234Z', 'Y*?([0-9]{1,3})');
Result: 1

在第一种情况下,整个正则表达式是贪婪的,因为Y*是贪婪的。它可以从Y开始匹配,并匹配从那里开始的最长可能字符串,即Y123。输出是该字符串中括号内的部分,即123。在第二种情况下,整个正则表达式是非贪婪的,因为Y*?是非贪婪的。它可以从Y开始匹配,并匹配从那里开始的最短可能字符串,即Y1。子表达式[0-9]{1,3}是贪婪的,但它不能改变关于整体匹配长度的决定;因此它被迫只匹配1

简而言之,当一个正则表达式包含贪婪和非贪婪子表达式时,总匹配长度要么尽可能长,要么尽可能短,这取决于分配给整个正则表达式的属性。分配给子表达式的属性只影响它们在彼此之间可以“吃掉”多少匹配内容。

量词{1,1}{1,1}?可分别用于强制子表达式或整个正则表达式的贪婪或非贪婪属性。这在你需要整个正则表达式具有与从其元素推断出的属性不同的贪婪属性时很有用。例如,假设我们正在尝试将包含一些数字的字符串分成数字以及数字之前和之后的部分。我们可能会尝试像这样来做

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

这没有用:第一个.*是贪婪的,所以它“吃掉”了尽可能多的内容,把\d+留在了最后可能的匹配位置,即最后一个数字。我们可能会尝试通过使它非贪婪来修复它

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

这也没有用,因为现在整个正则表达式是非贪婪的,所以它尽可能快地结束整体匹配。我们可以通过强制整个正则表达式为贪婪来获得我们想要的结果

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

独立于其组件的贪婪属性控制正则表达式的整体贪婪属性,这使得在处理可变长度模式方面具有很大的灵活性。

在决定更长或更短的匹配时,匹配长度是以字符而不是排序元素来衡量的。空字符串被认为比没有匹配更长。例如:bb*匹配abbbc的三个中间字符;(week|wee)(night|knights)匹配weeknights的所有十个字符;当(.*).*abc匹配时,括号内的子表达式匹配所有三个字符;当(a*)*bc匹配时,整个正则表达式和括号内的子表达式都匹配空字符串。

如果指定了不区分大小写匹配,则效果与字母表中的所有大小写区分都消失了差不多。当以多种大小写形式存在的字母字符作为括号表达式之外的普通字符出现时,它实际上被转换为包含两种大小写的括号表达式,例如,x变为[xX]。当它出现在括号表达式内部时,它的所有大小写对应项都被添加到括号表达式中,例如,[x]变为[xX],而[^x]变为[^xX]

如果指定了区分换行符的匹配,则.和使用^的括号表达式永远不会匹配换行符(因此匹配不会跨越行,除非正则表达式明确包含换行符),并且^$将在换行符之后和之前分别匹配空字符串,除了分别匹配字符串的开头和结尾之外。但 ARE 转义符\A\Z继续匹配字符串的开头或结尾。此外,字符类简写\D\W将匹配换行符,无论此模式如何。(在PostgreSQL 14 之前,它们在区分换行符模式下不会匹配换行符。编写[^[:digit:]][^[:word:]]以获得旧的行为。)

如果指定了部分区分换行符匹配,则这会影响.和括号表达式,就像区分换行符匹配一样,但不影响^$

如果指定了反向部分区分换行符匹配,则这会影响^$,就像区分换行符匹配一样,但不影响.和括号表达式。这不太有用,但为了对称性而提供。

9.7.3.6. 限制和兼容性 #

此实现中对正则表达式的长度没有特别限制。但是,旨在高度可移植的程序不应使用超过 256 字节的正则表达式,因为符合 POSIX 的实现可能会拒绝接受此类正则表达式。

ARE 中唯一与 POSIX ERE 实际不兼容的功能是\在括号表达式内部不会失去其特殊意义。所有其他 ARE 功能都使用在 POSIX ERE 中是非法或具有未定义或未指定效果的语法; 指令的***语法同样在 POSIX BRE 和 ERE 的语法之外。

许多 ARE 扩展是从 Perl 借鉴的,但有些已经过修改以清理它们,并且缺少一些 Perl 扩展。值得注意的不兼容之处包括\b\B、对尾随换行符的特殊处理的缺乏、将补括号表达式添加到受区分换行符匹配影响的事物中、对先行/后行约束中的括号和反向引用进行的限制以及最长/最短匹配(而不是首次匹配)的匹配语义。

9.7.3.7. 基本正则表达式 #

BRE 在几个方面与 ERE 不同。在 BRE 中,|+?是普通字符,并且没有等效的功能。边界的分隔符是\{\},其中{}本身是普通字符。嵌套子表达式的括号是\(\),其中()本身是普通字符。^是普通字符,除非它出现在正则表达式的开头或括号内的子表达式的开头,$是普通字符,除非它出现在正则表达式的结尾或括号内的子表达式的结尾,并且*是普通字符,如果它出现在正则表达式的开头或括号内的子表达式的开头(在可能的领先^之后)。最后,单数字反向引用可用,并且\<\>分别是[[:<:]][[:>:]]的同义词;BRE 中没有其他转义可用。

9.7.3.8. 与 SQL 标准和 XQuery 的区别 #

自 SQL:2008 以来,SQL 标准包含正则表达式运算符和函数,它们根据 XQuery 正则表达式标准执行模式匹配

  • LIKE_REGEX

  • OCCURRENCES_REGEX

  • POSITION_REGEX

  • SUBSTRING_REGEX

  • TRANSLATE_REGEX

PostgreSQL 目前没有实现这些运算符和函数。你可以在表 9.25中所示的每个情况下获得近似等效的功能。(此表中省略了双方的各种可选子句。)

表 9.25. 正则表达式函数等效性

SQL 标准 PostgreSQL
string LIKE_REGEX pattern regexp_like(string, pattern)string ~ pattern
OCCURRENCES_REGEX(pattern IN string) regexp_count(string, pattern)
POSITION_REGEX(pattern IN string) regexp_instr(string, pattern)
SUBSTRING_REGEX(pattern IN string) regexp_substr(string, pattern)
TRANSLATE_REGEX(pattern IN string WITH replacement) regexp_replace(string, pattern, replacement)

与 PostgreSQL 提供的类似的正则表达式函数也存在于许多其他 SQL 实现中,而 SQL 标准函数并没有得到广泛的实现。正则表达式语法的某些细节可能在每个实现中有所不同。

SQL 标准运算符和函数使用 XQuery 正则表达式,这些表达式与上面描述的 ARE 语法非常接近。现有的基于 POSIX 的正则表达式功能和 XQuery 正则表达式之间的主要区别包括

  • 不支持 XQuery 字符类减法。此功能的一个示例是使用以下代码来匹配只有英文辅音:[a-z-[aeiou]]

  • 不支持 XQuery 字符类简写\c\C\i\I

  • 不支持使用\p{UnicodeProperty}或反向\P{UnicodeProperty}的 XQuery 字符类元素。

  • POSIX 根据当前区域设置解释字符类,例如\w(参见表 9.21)。XQuery 通过引用 Unicode 字符属性来指定这些类,因此只有在遵循 Unicode 规则的区域设置中才能获得等效的行为。

  • SQL 标准(而不是 XQuery 本身)试图满足比 POSIX 更多的换行符变体。上面描述的区分换行符匹配选项只考虑 ASCII NL (\n) 作为换行符,但 SQL 希望我们也将 CR (\r)、CRLF (\r\n)(Windows 风格的换行符)和一些仅 Unicode 字符(如 LINE SEPARATOR (U+2028))视为换行符。值得注意的是,根据 SQL,.\s应该将\r\n计为一个字符而不是两个。

  • 表 9.20中描述的字符输入转义符中,XQuery 只支持\n\r\t

  • XQuery 不支持括号表达式内字符类的[:name:]语法。

  • XQuery 没有先行或后行约束,也没有表 9.22中描述的任何约束转义符。

  • 第 9.7.3.4 节中描述的元语法形式在 XQuery 中不存在。

  • XQuery 定义的正则表达式标志字母与 POSIX 的选项字母 (表 9.24) 相关,但并不相同。虽然 iq 选项的行为相同,但其他选项则不同。

    • XQuery 的 s(允许点匹配换行符)和 m(允许 ^$ 在换行符处匹配)标志提供了与 POSIX 的 npw 标志相同的功能,但它们的行为 与 POSIX 的 sm 标志相同。特别要注意,点匹配换行符是 POSIX 中的默认行为,但在 XQuery 中则不是。

    • XQuery 的 x(忽略模式中的空格)标志与 POSIX 的扩展模式标志明显不同。POSIX 的 x 标志还允许 # 在模式中开始注释,并且 POSIX 不会忽略反斜杠后的空格字符。

提交更正

如果您在文档中看到任何不正确的内容,不符合您对特定功能的体验,或者需要进一步澄清,请使用 此表格 报告文档问题。