支持的版本: 当前 (17) / 16 / 15 / 14 / 13
开发版本: devel
不支持的版本: 12 / 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。(正如预期的那样,如果 LIKE 返回 true,则 NOT LIKE 表达式返回 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 个带括号的子表达式匹配的源子字符串,并且可以包含 \& 表示应该插入与整个模式匹配的子字符串。如果需要在替换文本中放入字面反斜杠,请写 \\patternstring 中搜索,通常从字符串的开头开始,但如果提供了 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 split 函数会忽略出现在字符串开头或结尾或紧接在前一个匹配项之后的零长度匹配项。这与由其他 regexp 函数实现的 regexp 匹配的严格定义相反,但在实践中通常是最方便的行为。其他软件系统(如 Perl)使用类似的定义。

regexp_substr 函数返回与 POSIX 正则表达式模式匹配的子字符串,如果没有匹配项,则返回 NULL。它的语法是 regexp_substr(string, pattern [, start [, N [, flags [, subexpr ]]]])。patternstring 中搜索,通常从字符串的开头开始,但如果提供了 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 等编程语言中的可用性而得到广泛使用。RE使用这些非 POSIX 扩展的 s 在本文档中称为高级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 次(包含 n)的序列;m 不能大于 n
*? * 的非贪婪版本
+? + 的非贪婪版本
?? ? 的非贪婪版本
{m}? {m} 的非贪婪版本
{m,}? {m,} 的非贪婪版本
{m,n}? {m,n} 的非贪婪版本

使用 {...} 的形式被称为界限。界限内的数字 mn 是无符号十进制整数,允许的值从 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 字符类的任何字符,即任何字母、数字或下划线。这是一个扩展,与 1003.2 兼容但不指定,并且在打算移植到其他系统的软件中应谨慎使用。下面描述的约束转义通常是首选;它们并不更标准,但更易于键入。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。子表达式必须完全位于正则表达式中后向引用的前面。子表达式按照其前导括号的顺序编号。非捕获括号不定义子表达式。后向引用仅考虑被引用子表达式匹配的字符串字符,而不考虑其中包含的任何约束。例如,(^\d)\1 将匹配 22

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

转义 描述
\a 警报(响铃)字符,如在 C 中
\b 退格键,如在 C 中
\B 反斜杠 (\) 的同义词,有助于减少对反斜杠重复的需要
\cX (其中 X 是任意字符)其低 5 位与 X 的低 5 位相同,且其他位均为零的字符
\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-fA-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. 正则表达式元语法 #

除了上面描述的主要语法外,还有一些特殊的格式和杂项语法工具可用。

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

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

表 9.24. ARE 嵌入选项字母

选项 描述
b 正则表达式的其余部分是 BRE
c 大小写敏感匹配(覆盖运算符类型)
e 正则表达式的其余部分是 ERE
i 大小写不敏感匹配(请参阅第 9.7.3.5 节)(覆盖运算符类型)
m n 的历史同义词
n 换行符敏感匹配(请参阅第 9.7.3.5 节
p 部分换行符敏感匹配(请参阅第 9.7.3.5 节
q 正则表达式的其余部分是文字(带引号)字符串,所有都是普通字符
s 非换行符敏感匹配(默认)
t 严格语法(默认;请参阅下文)
w 反向部分换行符敏感(怪异)匹配(请参阅第 9.7.3.5 节
x x

扩展语法(请参阅下文)

嵌入选项在终止序列的 ) 处生效。它们只能出现在 ARE 的开头(如果有 ***: 指示符,则在它之后)。

  • 空白字符或前面带有反斜杠 \# 会被保留。

  • 在方括号表达式内的空白或 # 会被保留。

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

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

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

如果初始 ***= 指令指定将用户输入视为字面字符串而不是正则表达式,则这些元语法扩展均不可用

9.7.3.5. 正则表达式匹配规则 #

如果一个正则表达式可以匹配给定字符串中的多个子字符串,则该正则表达式会匹配字符串中最早开始的那个。如果该正则表达式可以匹配从该点开始的多个子字符串,则会选取最长匹配或最短匹配,具体取决于该正则表达式是贪婪的还是非贪婪的

一个正则表达式是否贪婪由以下规则确定:

  • 大多数原子和所有约束都没有贪婪属性(因为它们无论如何都无法匹配可变数量的文本)。

  • 在正则表达式周围添加括号不会改变其贪婪性。

  • 具有固定重复限定符({m}{m}?)的量化原子与其本身具有相同的贪婪性(可能没有)。

  • 具有其他正常限定符(包括 {m,n},其中 m 等于 n)的量化原子是贪婪的(倾向于最长匹配)。

  • 具有非贪婪限定符(包括 {m,n}?,其中 m 等于 n)的量化原子是非贪婪的(倾向于最短匹配)。

  • 分支 - 即没有顶层 | 运算符的正则表达式 - 与其中第一个具有贪婪属性的量化原子具有相同的贪婪性。

  • | 运算符连接的两个或多个分支组成的正则表达式始终是贪婪的。

以上规则不仅将贪婪属性与各个量化原子相关联,还将贪婪属性与包含量化原子分支和整个正则表达式相关联。这意味着匹配是以这样的方式完成的:分支或整个正则表达式整体匹配最长或最短的子字符串。一旦确定了整个匹配的长度,则根据该子表达式的贪婪属性确定匹配任何特定子表达式的部分,其中正则表达式中较早开始的子表达式优先于较晚开始的子表达式。

以下是一个例子,说明了这意味着什么:

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(字符串, 模式)
TRANSLATE_REGEX(模式 IN 字符串 WITH 替换) regexp_replace(字符串, 模式, 替换)

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

SQL 标准运算符和函数使用 XQuery 正则表达式,它与上述 ARE 语法非常接近。现有基于 POSIX 的正则表达式功能与 XQuery 正则表达式之间的显著差异包括:

  • 不支持 XQuery 字符类减法。此功能的一个示例是使用以下内容仅匹配英语辅音:[a-z-[aeiou]]

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

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

  • POSIX 根据当前的区域设置(您可以通过将 COLLATE 子句附加到运算符或函数来控制区域设置)解释字符类,例如 \w(请参见表 9.21)。XQuery 通过引用 Unicode 字符属性来指定这些类,因此只有使用遵循 Unicode 规则的区域设置才能获得等效的行为。

  • SQL 标准(而不是 XQuery 本身)试图适应比 POSIX 更多的 换行符 变体。上述换行符敏感匹配选项仅将 ASCII NL (\n) 视为换行符,但 SQL 会将 CR (\r)、CRLF (\r\n) (Windows 样式的换行符) 以及一些仅限 Unicode 的字符(如行分隔符 (U+2028))也视为换行符。值得注意的是,根据 SQL,.\s 应将 \r\n 算作一个字符而不是两个。

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

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

  • 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 不会忽略反斜杠后的空格字符。

提交更正

如果您在文档中发现任何不正确、与您使用特定功能的经验不符或需要进一步澄清的内容,请使用此表单来报告文档问题。