↑↓ 选择↵ 打开⌫ 切换范围完整搜索

PG.CENTER 连接 PostgreSQL 文档、百科与生态知识。由 Pigsty 维护。

支持中的版本: 当前版本 (18) / 17 / 16 / 15 / 14
开发中的版本: 19 / 20devel
已结束支持的版本: 13 / 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]

如果该 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 节。

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

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

可以用关键字 ILIKE 代替 LIKE,使匹配根据当前区域设置忽略大小写。这不属于 SQL 标准,而是 PostgreSQL 的扩展。

操作符~~等效于 LIKE,而~~* 对应 ILIKE。还有 !~~和!~~* 操作符分别代表 NOT LIKE 和 NOT ILIKE。所有这些操作符都是 PostgreSQL 特有的。你可能会在 EXPLAIN 输出和类似的地方看到这些操作符名称,因为解析器实际上将 LIKE 等翻译成这些操作符。

在 PostgreSQL 语法中,LIKE、ILIKE、NOT LIKE 和 NOT ILIKE 通常被当作操作符;例如,它们可以用于 expression operator ANY (subquery) 构造,但其中不能包含 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 操作符根据其模式是否匹配给定字符串返回真或假。它与 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 一样,指定的模式必须匹配整个数据字符串,否则函数失败并返回空值。为了指明要提取的子字符串所对应的模式部分,模式中应包含两个分隔符,每个分隔符都由转义字符后跟双引号(")组成。匹配成功时,返回与这两个分隔符之间的模式部分匹配的文本。

转义双引号分隔符实际上会把 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 ~ text → boolean

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

'thomas' ~ 't.*ma' → t

text ~* text → boolean

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

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

text !~ text → boolean

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

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

text !~* text → boolean

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

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


POSIX 正则表达式提供了比 LIKE 和 SIMILAR TO 操作符更强大的模式匹配方式。许多 Unix 工具,例如 egrep、sed 或 awk,都使用与这里描述的模式匹配语言相似的语言。

正则表达式是一个字符序列,是定义一组字符串(一个正则集)的简写。如果字符串属于正则表达式描述的正则集,就称该字符串匹配此正则表达式。与 LIKE 一样,模式中的字符精确匹配字符串中的字符,除非该模式字符在正则表达式语言中有特殊含义 — 但正则表达式使用的特殊字符与 LIKE 不同。与 LIKE 模式不同,正则表达式可以匹配字符串中的任意位置,除非显式将其锚定到字符串开头或末尾。

一些示例:

'abcd' ~ 'bc'     true
'abcd' ~ 'a.c'    true — 点号匹配任意单个字符
'abcd' ~ 'a.*d'   true — * 重复前一个模式项
'abcd' ~ '(b|x)'  true — | 表示 OR,圆括号用于分组
'abcd' ~ '^a'     true — ^ 锚定到字符串开头
'abcd' ~ '^(b|c)' false — 如果没有这个锚定本来会匹配

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

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

一些示例:

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

regexp_count 函数计算 POSIX 正则表达式模式与字符串匹配的次数。它的语法为 regexp_count(string, pattern [, start [, flags ]])。pattern 在 string 中进行搜索,通常从字符串的开头开始,但如果提供了 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 参数或将其指定为 0,则函数返回匹配项的第一个字符的位置。否则,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 函数返回一个 text 数组,包含字符串首次匹配 POSIX 正则表达式模式时匹配的子字符串。它的语法为 regexp_match(string, pattern [, flags ])。如果没有匹配,结果为 NULL。如果找到匹配,并且 pattern 不包含圆括号子表达式,则结果是包含与整个模式匹配的子字符串的单元素 text 数组。如果找到匹配,并且 pattern 包含圆括号子表达式,则结果是一个 text 数组,其中第 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()。然而,在 PostgreSQL 版本 15 及以上版本中才存在 regexp_substr()。在旧版本中工作时,你可以提取 regexp_match() 结果的第一个元素,例如:

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

regexp_matches 函数返回一个 text 数组的集合,其中包含与 POSIX 正则表达式模式匹配的子字符串。它具有与 regexp_match 相同的语法。如果没有匹配,则此函数不返回任何行;如果有匹配且未给出 g 标志,则返回一行;如果有 N 个匹配且给出 g 标志,则返回 N 行。每个返回的行都是一个 text 数组,其中包含整个匹配的子字符串或与 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;

如果有一个匹配,则这个语句会产生一个 text 数组,否则返回 NULL,这和 regexp_match() 的做法一样。如果没有子查询,这个查询对于没有匹配的表行根本不会产生输出,这通常不是预期行为。

regexp_replace 函数将匹配 POSIX 正则表达式模式的子字符串替换为新文本。它的语法为 regexp_replace(source, pattern, replacement [, start [, N ]] [, flags ])。(注意,只有指定了 start,才能指定 N,但 flags 在任何情况下都可以给出。)如果没有匹配 pattern 的内容,则原样返回源字符串 source。如果有匹配,则返回将匹配的子字符串替换为 replacement 后的 source。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)

正如最后一个示例所示,正则表达式分割函数会忽略出现在字符串开头或结尾或紧跟在前一个匹配项之后的零长度匹配。这与其他正则表达式函数实现的严格的正则表达式匹配定义相矛盾,但在实践中通常是最方便的行为。其他软件系统如 Perl 使用类似的定义。

regexp_substr 函数返回与 POSIX 正则表达式模式匹配的子字符串,如果没有匹配,则返回 NULL。它的语法为 regexp_substr(string, pattern [, start [, N [, flags [, subexpr ]]]])。pattern 在 string 中进行搜索,通常从字符串的开头开始,但如果提供了 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 写的一个包来实现的。下面的正则表达式的大部分描述都是从他的手册页中逐字拷贝过来的。

正则表达式(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 规则。不过,也可以像第 9.7.3.4 节中所述那样,在 RE 模式前加上一个嵌入选项,从而改用限制更多的 ERE 或 BRE 规则。这对于需要精确遵循 POSIX 1003.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} 一个由原子的从 m 次到 n 次(包括)匹配组成的序列;m 不能超过 n
*? * 的非贪婪版本
+? +的非贪婪版本
?? ?的非贪婪版本
{m}? {m}的非贪婪版本
{m,}? {m,}的非贪婪版本
{m,n}? {m,n}的非贪婪版本

使用{...}的形式被称作范围。一个范围内的数字 m 和 n 都是无符号十进制整数,允许的数值从 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. 方括号表达式 #

方括号表达式是用[] 括起来的字符列表。通常,它匹配列表中的任意单个字符(但请参见下文)。如果列表以^开头,则匹配任意不在列表剩余部分中的单个字符。如果列表中的两个字符用-分隔,则表示排序序列中这两个字符之间的完整字符范围(包含两个端点);例如,ASCII 中的[0-9] 匹配任意十进制数字。两个范围共享一个端点是不合法的,例如 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 加上下划线(_),以及恰好包含 7 位 ASCII 字符集的 ascii 字符类。

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

9.7.3.3. 正则表达式转义 #

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

字符输入转义便于在 RE 中指定不可打印或其他不便输入的字符,见表 9.20。

字符类简写转义用来提供一些常用的字符类简写。它们显示在表 9.21 中。

约束转义是以转义形式书写的约束,在满足特定条件时匹配空字符串,见表 9.22。

反向引用(\n)匹配前面编号为 n 的圆括号子表达式所匹配的同一字符串(参阅表 9.23)。例如,([bc])\1 匹配 bb 或者 cc,但是不匹配 bc 或者 cb。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-9、a-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 以***:开头,余下部分就被视为 ARE。(这在 PostgreSQL 中通常没有影响,因为 RE 默认被视为 ARE;但如果通过正则表达式函数的 flags 参数指定了 ERE 或 BRE 模式,它就会产生影响。)如果 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 可以分段并添加注释。此基本规则有三个例外:

  • 空白字符或 # 的前面若有 \,该字符就会被保留。

  • 方括号表达式里的空白或者#将被保留

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

在这里,空白字符包括空格、制表符、换行符,以及属于 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})');
结果: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_match('abc01234xyz', '(.*)(\d+)(.*)');
结果:{abc0123,4,xyz}

这不会有用:第一个.* 是贪婪的,因此它会“吃掉”尽可能多的字符而留下\d+去匹配在最后一个可能位置上的最后一个数字。我们可能会通过让它变成非贪婪来修复:

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

这也不会有用:因为现在 RE 作为整体来说是非贪婪的,因此它会尽快结束全部的匹配。我们可以通过强制 RE 整体是贪婪的来得到我们想要的:

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

将 RE 的整体贪婪性与各组件自身的贪婪性分开控制,为处理变长模式提供了很大的灵活性。

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

如果指定不区分大小写的匹配,其效果近似于字母表中的所有大小写差别都消失了。当存在大小写形式的字母作为普通字符出现在方括号表达式之外时,实际上会转换为包含其大小写形式的方括号表达式,例如 x 变成[xX]。当它出现在方括号表达式内部时,其所有大小写形式都会加入该表达式,例如[x] 变成[xX],[^x] 变成[^xX]。

如果指定了换行敏感的匹配,.和使用^的方括号表达式将永远不会匹配换行字符(这样,匹配就不会跨越行,除非 RE 显式地包含了换行)并且^和$除了分别匹配串开头和结尾之外,还将分别匹配换行后面和前面的空串。但是 ARE 转义\A 和\Z 仍然只匹配串的开头和结尾。此外,字符类简写\D 和\W 将匹配换行,而无论这种模式如何。(在 PostgreSQL 14 之前,它们在换行敏感模式下不匹配换行。写入[^[:digit:]] 或 [^[:word:]] 以获取旧的行为。)

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

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

9.7.3.6. 限制和兼容性 #

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

ARE 与 POSIX ERE 实际不兼容的唯一特性是:\ 在方括号表达式中不会失去特殊含义。其他所有 ARE 特性所使用的语法,在 POSIX ERE 中都是非法的,或其效果未定义或未指定;引导前缀的*** 语法同样不属于 POSIX 的 BRE 或 ERE 语法。

许多 ARE 扩展借鉴自 Perl,但其中一些经过了整理和修改,也未实现少数 Perl 扩展。需要注意的不兼容之处包括\b、\B、不对末尾换行符作特殊处理、取反的方括号表达式也受换行敏感匹配影响、先行和后行约束中对圆括号及反向引用的限制,以及采用最长或最短匹配而非首次匹配的语义。

9.7.3.7. 基本正则表达式 #

BRE 在几个方面和 ERE 不太一样。在 BRE 中,|、+和?都是普通字符并且没有与它们功能等价的东西。范围的定界符是\{和\},而{和}本身是普通字符。嵌套的子表达式的圆括号是\(和\),而(和) 自身是普通字符。除非在 RE 开头或者是圆括号子表达式开头,^都是一个普通字符。除非在 RE 结尾或者是圆括号子表达式的结尾,$是一个普通字符。如果* 出现在 RE 开头或者是圆括号封装的子表达式开头(前面可能有^),那么它是个普通字符。最后,可以用单数字的反向引用,\<和\> 分别是[[:<:]] 和[[:>:]] 的同义词;在 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 patternregexp_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);你可以通过为操作符或函数附加 COLLATE 子句来控制该区域设置。XQuery 根据 Unicode 字符属性定义这些类,因此只有使用遵循 Unicode 规则的区域设置时,才能得到等效行为。

  • SQL 标准(而不是 XQuery 本身)试图适应比 POSIX 更多的“换行符”变体。上面描述的换行符敏感匹配选项只考虑 ASCII NL(\n)为换行符,但 SQL 要求我们将 CR(\r)、CRLF(\r\n)(Windows 风格的换行符)以及一些仅限于 Unicode 的字符,如 LINE SEPARATOR(U+2028),也视为换行符。值得注意的是,.和\s 应该根据 SQL 的规定将\r\n 视为一个字符而不是两个字符。

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

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

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

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

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

    • XQuery 的 s(允许点匹配换行符)和 m(允许^ 和$在换行处匹配)标志提供与 POSIX 的 n、p 和 w 标志相同的行为,但它们不匹配 POSIX 的 s 和 m 标志的行为。特别注意,点匹配换行符是 POSIX 的默认行为,但不是 XQuery 的。

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

报告文档问题

阅读 上游文档. 通过 PostgreSQL 文档反馈表单.