9.7. 模式匹配

PostgreSQL提供了三种实现模式匹配的方法:传统SQLLIKE操作符、SQL99 新增的SIMILAR TO操作符、POSIX风格的正则表达式。另外还有一个模式匹配函数substring可用, 它可以使用SIMILAR TO风格或者POSIX风格的正则表达式。 除了基本" ?"操作符,函数可用于提取或替换匹配的子串和分隔匹配位置的字符串。

Tip: 如果你的模式匹配要求比这些还多,或者想写一些模式驱动的替换和转换,请考虑用Perl或Tcl写一个用户定义函数。

9.7.1. LIKE

stringLIKEpattern[ESCAPEescape-character]
stringNOT LIKEpattern[ESCAPEescape-character]

如果该string与所提供的pattern模式匹配, 那么LIKE表达式返回真。和我们想像的一样,如果LIKE返回真, 那么NOT LIKE表达式将返回假,反之亦然。一个等效的表达式 是NOT(stringLIKEpattern)

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

一些例子:

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

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

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

请注意反斜杠在字符串文本里已经有特殊含义了,所以如果你写一个包含反斜杠的模式常量, 那你就要在 SQL 语句里写两个反斜杠。因此,写一个匹配单个反斜杠的模式实际上要在语句里 写四个反斜杠。你可以通过用ESCAPE选择一个不同的逃逸字符来避免这样;这样反斜杠就 不再是LIKE的特殊字符了。(但仍然是字符文本分析器的特殊字符,所以你还是需要两个反斜杠。)

我们也可以通过写成ESCAPE ''的方式关闭逃逸机制,这时,我们就不能关闭下划线和百分号的特殊含义

关键字ILIKE可以用于替换LIKE, 令该匹配就当前的区域设置是大小写无关的。这个特性不是SQL标准, 是PostgreSQL扩展。

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

9.7.2. SIMILAR TO正则表达式

stringSIMILAR TOpattern[ESCAPEescape-character]
stringNOT SIMILAR TOpattern[ESCAPEescape-character]

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

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

除了这些从 LIKE 借用的功能之外,SIMILAR TO支持下面这些从 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(string from pattern for escape-character)函数提供了 一个从字符串中抽取一个匹配 SQL 正则表达式模式的子字符串功能。和SIMILAR TO一样, 声明的模式必须匹配整个字符串,否则函数失效并返回 NULL 。为了标识在成功的时候应该返回的模式部分, 模式必须出现后跟双引号(")的两个逃逸字符。匹配这两个标记之间的模式的字符串将被返回。

一些例子,用#"分隔返回字符串:

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

9.7.3. POSIX正则表达式

Table 9-11列出了所可用的POSIX 正则表达式的模式匹配操作符

Table 9-11. 正则表达式匹配操作符

操作符描述示例
~匹配正则表达式,区分大小写'thomas' ~ '.*thomas.*'
~*匹配正则表达式,不分大小写'thomas' ~* '.*Thomas.*'
!~不匹配正则表达式,区分大小写'thomas' !~ '.*Thomas.*'
!~*不匹配正则表达式,不分大小写'thomas' !~* '.*vadim.*'

POSIX正则表达式提供了比LIKESIMILAR TO 操作符更强大的模式匹配的方法。许多 Unix 工具,比如egrepsedawk使用类似的模式匹配语言。

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

一些例子:

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

下面更详细描述POSIX模式匹配语言。

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

一些例子:

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

regexp_replace(sourcepatternreplacement [flags]) 函数提供了将匹配 POSIX 正则表达式模式的子字符串替换为新文本的功能。如果没有匹配pattern的子字符串, 那么返回不加修改的source字符串。如果有匹配,则返回的source字符串里面的对应子字符串 将被 replacement 字符串替换掉。replacement字符串可以包含\n, 这里的n19, 表明源字符串中匹配第n个圆括弧子表达式的部分将插入在该位置,并且它可以包含\&表示 应该插入匹配整个模式的字符串。如果你需要放一个文本反斜杠在替换文本里,那么写\\(和 通常一样,记得在文本常量字符串里写双反斜杠)。可选的flags参数包含零个或多个改变函数行为的单字母标记。 i表示进行大小写无关的匹配,g表示替换每一个匹配的子字符串而不仅仅是第一个。 其它支持的标记描述在Table 9-19

一些例子:

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正则表达式的获取子串结果的text数组。使用语法 regexp_matches(stringpattern [flags])。这个函数可以返回没有行,一行,或者多行 (参阅下面的g标记)。如果pattern没有匹配,则函数返回没有行。 如果模式包含没有括号的子表达式,则每行返回的是单元素的文本数组,其中包含的子串相匹配整个模式。 如果模式包含括号的子表达式,函数返回一个文本数组,n的元素是子串匹配n括号子表达式内的模式。 (不计"非捕获"的括号);详细信息参阅下面的。 参数flags是一个选项text字符串,含有0或者更多单字母标记来改变函数行为。标记g导致查找字符串中的每个匹配, 而不仅是第一个,每个匹配返回一行,其它的标记支持描述在Table 9-19

一些例子:

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)

使用select子句,可能强制regexp_matches()总是返回一行; 在SELECT当你想要的所有行的目标列表返回,甚至不匹配的情况下,是有特别有用的。

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

regexp_split_to_table函数使用POSIX正则表达式作为分隔符,分隔字符串。 语法regexp_split_to_table(stringpattern [flags])。如果没有匹配pattern,函数将返回string。 如果有至少一个匹配,每个匹配返回从最后一个匹配结束(或者字符串的开头)到匹配开始的文本。 当没有更多的匹配,返回最后一个匹配的结束到字符串的结束的文本。 flags参数是一个选项text字符串,含有0或者更多单字母标记来改变函数行为。 regexp_split_to_table支持的标记描述在Table 9-19

除了regexp_split_to_array 返回结果为text数组,regexp_split_to_array函数行为与regexp_split_to_table相同, 使用语法 regexp_split_to_array(stringpattern [flags])。 参数与regexp_split_to_table相同。

一些例子:


SELECT foo FROM regexp_split_to_table('the quick brown fox jumped over the lazy dog', E'\\s+') AS foo;
  foo   
--------
 the    
 quick  
 brown  
 fox    
 jumped 
 over   
 the    
 lazy   
 dog    
(9 rows)

SELECT regexp_split_to_array('the quick brown fox jumped over the lazy dog', E'\\s+');
              regexp_split_to_array             
------------------------------------------------
 {the,quick,brown,fox,jumped,over,the,lazy,dog}
(1 row)

SELECT foo FROM regexp_split_to_table('the quick brown fox', E'\\s*') AS foo;
 foo 
-----
 t         
 h         
 e         
 q         
 u         
 i         
 c         
 k         
 b         
 r         
 o         
 w         
 n         
 f         
 o         
 x         
(16 rows)

作为最后一个例子表明,在出现在字符串的开始或结束 或在紧接一前一后的匹配,正则表达式分隔函数忽略零长度匹配, 这样实现regexp_matches严格上来说是违背了的 正则表达式匹配的定义,但在实际使用中,通常是最便利的的行为。 如Perl等软件系统,使用了类似的定义。

9.7.3.1. 正则表达式细节

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

正则表达式(REs),在POSIX1003.2中定义, 它有两种形式:扩展正则表达式extendedREs或 EREs(基本上就是在egrep里的那些), 基本正则表达式basicREs或 BREBREs(基本上就是在 eded里的那些)。 PostgreSQL支持这两种形式,也实现了一些POSIX 里面没有的,在 Perl 或Tcl 这样的语言中得到广泛应用的类似扩展。使用了那些非 POSIX 扩展的正则表达式叫 高级正则表达式advancedREs或AREs。 AREs几乎完全是EREs的超集,但是BREs有几个符号上的不兼容(以及更多的限制)。 我们首先描述AREs和EREs形式, 描述那些只适用于AREs的特性,然后描述与BREs的区别是什么。

Note: PostgreSQL总是初始化一个遵循ARE规则的正则表达式。然而对于RE模式,可以在 更多限制的ERE或BRE规则前面,选择一个embedded option,描述在Section 9.7.3.4。 这对于兼容期望完全POSIX1003.2规则的应用程序,是有用的。

一条正则表达式可以定义为一个或多个的分支,由|分隔。 它要匹配其中任何一个分支。

一个分支是0或多个限定的原子quantified atoms或约束constraints连接而成。 一个原子匹配第一个,后面的原子匹配第二个,依次类推。一个空的分支要匹配空字符串。

一个有限定的原子是一个原子atom,后面可能跟着一个界定符quantifier。没有界定符的时候, 它匹配一个原子,有界定符的时候,它可以匹配若干个原子。 原子的各种可能性,在atomTable 9-12里面显示。可能的界定符和他们的含义在Table 9-13里显示。

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

Table 9-12. 正则表达式原子

原子描述
(re) (re是任意正则表达式)匹配一个对 re 的匹配,有可报告的匹配信息。
(?:re)as above, but the match is not noted for reporting (a"non-capturing"set of parentheses) (AREs only) 同上,但是不会报告匹配信息(一个"非捕获"non-capturing""括号),只在 ARE 中有。
.匹配任意单个字符
[chars] 一个方括弧表达式bracket expression,匹配chars中的任意字符 (参阅Section 9.7.3.2获取更多细节)。
\k (k是非字母数字字符)匹配一个当作普通字符看待的特定字符,比如\\匹配一个反斜杠。
\c c是一个字母数字(可能跟着其它字符),首项是逃逸符escape,参阅Section 9.7.3.3。 (仅在ARE 中;在 ERE 和 BRE 中,它匹配c)。
{when followed by a character other than a digit, matches the left-brace character{; when followed by a digit, it is the beginning of a bound(see below) 如果后面跟着一个非数字字符,那么就 匹配左花括弧{;如果跟着一个数字,那么它是范围bound的开始(见下面)
x 这里的x是一个没有其它特征的单个字符,则匹配该字符。

RE不能以\结尾。

Note: 要记住反斜杠(\)在PostgreSQL字符串文本中已经有特殊含义了。 要写一个包含反斜杠的模式,你必须在语句里写两个反斜杠(参阅Section 4.1.2.1)。

Table 9-13. 正则表达式界定符

界定符匹配
*一个匹配0或更多个原子的序列
+一个匹配 1或更多个原子的序列
?一个匹配0或1个原子的序列
{m}一个正好匹配m个原子的序列
{m,}一个匹配m个或者更多原子的序列
{mn} 一个匹配mn个(包含两端)原子的序列;m不能比n
*?*的非贪婪模式
+?+的非贪婪模式
???的非贪婪模式
{m}? {m}的非贪婪模式
{m,}? {m,}的非贪婪模式
{mn}? {mn}的非贪婪模式

{...}的形式被称作范围bounds。 一个范围内的数字mn都是无符号十进制整数,允许的数值从 0 到 255 (闭区间)。

Non-greedy界定符(只在 ARE 中可用)匹配对应的正常(greedy)模式, 区别是它寻找最少的匹配,而不是最多的匹配。参阅Section 9.7.3.5获取更多细节。

Note: 一个界定符不能紧跟在另外一个界定符后面。例如,**是无效的。界定符不能是表达式或者子表达式的开头, 也不能跟在^|后面。

Table 9-14. 正则表达式约束

约束描述
^匹配字符串的开头
$匹配字符串的结尾
正向预查positive lookahead在任何匹配re的字符串开始处匹配查找字符串(只在 ARE 中有)。  
(?!re) 负向预查negative lookahead在任何不匹配re的字符串开始处匹配查找字符串(只在 ARE 中有)。

预查约束不能包含后引用back references(参阅Section 9.7.3.3),并且在其中的所有圆括号都被认为是不捕获的。

9.7.3.2. 方括弧表达式

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

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

在一个方括弧表达式里,一个集合元素(一个字符、一个当做一个字符的多字符序列、或者 一个表示上面两种情况的集合序列)包含在[..]里面的时候表示该集合元素的字符序列。 该序列是该方括弧列表的一个元素。因此一个包含多字符集合元素的方括弧表达式就可以 匹配多于一个字符,比如,如果集合序列包含一个ch集合元素,那么[[.ch.]]*c 匹配chchcc的头五个字符。 译注:其实把 [. 和 .] 括起来的整体当一个字符看就行了。

Note: PostgreSQL目前不支持多字节符集合元素。这些信息描述了将来可能有的行为。

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

在方括弧表达式里,在[::]里面括起来的字符表名,代表属于该表的所有字符的列表。 标准的字符表名字是:alnumalphablankcntrldigitgraphlowerprintpunctspaceupperxdigit。它们代表 在ctype里定义的字符表。 本地化设置可能会提供其它的表。字符表不能用做一个范围的端点。

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

9.7.3.3. 正则表达式逃逸

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

字符项逃逸Character-entry escapes用于方便我们声明正则表达式里那些不可打印和在RE里其它不方便的字符。 它们在Table 9-15里列出。

类缩写逃逸Class-shorthand escapes用来提供一些常用的字符类缩写。他们在Table 9-16里列出

约束逃逸constraint escape是一个约束,如果满足特定的条件,它匹配该空字符串,作为逃逸编写。 它们在Table 9-17里列出。

后引用back reference(\n)匹配数字n 指定前面圆括弧子表达式匹配的同一个字符串(参阅Table 9-18)。 例如,(([bc])\1匹配bbcc但是不匹配bccb。 正则表达式里的子表达式必须完全在后引用前面。非捕获圆括弧并不定义子表达式。

Note: 请记住,如果把模式当作一个 SQL 字符串常量输入,那么逃逸前导的\需要双倍地写。例如:

'123' ~ E'^\\d{3}'true

Table 9-15. 正则表达式字符项逃逸

逃逸描述
\a警笛(铃声)字符,和 C 里一样
\b退格,和 C 里一样
\B \的同义词,用于减少反斜杠加倍的需要
\cX (这里X是任意字符)字符的低5位和X里的相同,其它位都是0。
\e 集合序列名字是ESC的字符,如果不是,则是八进制值为033的字符
\f进纸,和C里一样
\n新行,和C里一样
\r回车,和C里一样
\t水平制表符,和C里一样
\uwxyz (这里的wxyz是恰好四个十六进制数字)本机字节序的 UTF-16(宽字符,16位)字符U+wxyz
\Ustuvwxyz(wherestuvwxyzis exactly eight hexadecimal digits) reserved for a hypothetical Unicode extension to 32 bits (这里的stuvwxyz是恰好八个十六进制数字)为Unicode 32 位扩展预留的。
\v垂直制表符,和 C 里一样
\xhhh (这里的hhh是一个十六进制序列)十六进制 值为0xhhh的字符(不管用了几个十六进制位,都是一个字符)。
\0 值为0的字符(空字节)
\xy (这里的xy是恰好两个八进制数字,并且不是一个back reference后引用)八进制值为0xy的字符
\xyz (这里的xyz是恰好三位八进制位,并且不是一个 后引用)八进制值为0xyz的字符

十六进制数字是0-9a-fA-F。八进制数字是0-7

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

Table 9-16. 正则表达式类缩写逃逸

逃逸描述
\d[[:digit:]]
\s[[:space:]]
\w[[:alnum:]_] (注意,这里是包含下划线的)
\D[^[:digit:]]
\S[^[:space:]]
\W[^[:alnum:]_] (注意,这里是包含下划线的)

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

Table 9-17. 正则表达式约束逃逸

逃逸描述
\A 只匹配字符串开头(参阅Section 9.7.3.5获取它和^区别的信息)
\m只匹配一个词的开头
\M只匹配一个词的结尾
\y只匹配一个词的开头或者结尾
\Y 只匹配那些既不是词的开头也不是词的结尾的点
\Z 只匹配一个字符串的结尾(参阅Section 9.7.3.5获取它和$区别的信息)

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

Table 9-18. 正则表达式后引用

逃逸描述
\m 这里的m是一个非零数字)一个指向第m个子表达式的后引用
\mnn (这里的m是一个非零数字,nn是更多的数字,并且十进制数值mnn 不能大于到这个位置为止的闭合捕获圆括号的个数)一个指向第 mnn 个子表达式的后引用。

Note: 在八进制字符项逃逸和后引用之间有一个历史继承的歧义存在,这个歧义是 通过启发分析解决的,像上面描述的那样。前导零总是表示这是一个八进制逃逸。 而单个非零数字,如果没有跟着任何其它数字,那么总是认为是后引用。 一个多数据位的非零开头的序列也认为是后引用(只要它在合适的子表达式后面, 也就是说,数值在后引用的合法范围内),否则就认为是一个八进制。

9.7.3.4. 正则表达式元语法

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

正则表达式可以用两个特殊意义的director前缀其中之一。 如果一个正则表达式以***:开头,正则表达式其余部分,可以作为ARE。 (在正则表达式作为ARE后,这通常在PostgreSQL没有影响的;但是 如果在ERE或BRE模式指定一个flags参数给正则表达式函数,确实是有影响的。) 如果一个正则表达式以***=开头,正则表达式其余部分作为文本字符串, 所有字符串视为普通字符。

ARE可以用embedded options开头;一个序列(?xyz) (这里xyz是1个或更多个字母字符)特定的选项影响正则表达式的其余部分。 这些选项覆盖任何先前确定的选项 —尤其,他们可以覆盖正则表达式操作符或flags 给正则表达式函数的隐含区分大小写的行为。可用的字母选项显示在Table 9-19。 注意同样这些字母选项可以用作正则表达式函数的flags参数。

Table 9-19. ARE 嵌入选项字母

选项描述
b正则表达式的其余部分是BRE
c大小写敏感匹配(覆盖操作符类型)
e正则表达式的其余部分是 ERE
i 大小写不敏感匹配(参阅Section 9.7.3.5)(覆盖操作符类型)
m n的历史同义词
n 换行敏感匹配(参阅Section 9.7.3.5
p 部分换行敏感匹配(参阅Section 9.7.3.5
q 正则表达式的其余部分为一个文本("quoted")字符串,所有都是普通字符
s非换行敏感匹配(缺省)
t严格的语法(缺省,见下文)
w 反部分换行敏感("怪异""weird")匹配(参阅Section 9.7.3.5)
x扩展的语法(见下文)

嵌入的选项在终止其序列的)发生作用。他们只在 ARE 的开始处起作用(即在任何***:引导符后面)。

除了通常情况下所有的选项字符都显然遵循(严格tight)正则表达式语法,还有 一种扩展expanded语法,可以通过声明嵌入的x选项使用。在扩展语法里, 正则表达式中的空白字符被忽略,就像那些在#和新行后(或正则表达式的结尾处)之间的所有字符一样。这样就允许 我们给一个复杂的正则表达式分段和注释。不过这个基本规则上有三种例外:

  • 前置了\的空白符或#保留

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

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

在这里,空白是空白、水平制表符、换行、和任何属于space(空白)字符表的字符。

最后,在 ARE 里,方括号表达式外面,序列(?#ttt)(这里的ttt是任意不包含) 的文本)是一个注释,完全忽略掉。再次,在多字符符号里,不允许使用,例如(?:。这样的注释是比一个有用的工具更历史些, 他们的用法已经废弃了;我们应该使用扩展语法代替他。

如果声明了一个初始化的***=引导符,那么所有这些元语法扩展都不能使用, 因为这样表示把用户输入当作一个文本字符串而不是正则表达式对待。

9.7.3.5. 正则表达式匹配规则

在正则表达式可以匹配给出的字符串中多于一个子串的情况下,正则表达式 匹配字符串中最早开始匹配的那个子串。如果正则表达式可以匹配多个子串在那些位置开始处,要么是匹配最长的, 要么是最短的,具体哪种,取决于正则表达式是贪婪greedy的还是非贪婪non-greedy的。

一个正则表达式是否贪婪取决于下面规则:

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

  • 在一个正则表达式周围加上圆括号并不会改变其贪婪性。

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

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

  • 一个带非贪婪量词(包括{mn}?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

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

量词{1,1}{1,1}?可以分别用于在一个子表达式或者整个正则表达式上强制贪婪或者非贪婪。

匹配长度是以字符衡量的,而不是集合的元素。一个空字符串会被认为 比什么都不匹配长。比如:bb*匹配abbbc的中间三个字符; (week|wee)(night|knights)匹配weeknights的所有十个字符; 而(.*).*匹配abc的时候,圆括号包围的子表达式匹配所有三个字符; 而如果用(a*)*匹配bc,那么整个正则表达式和圆括号子表达式都匹配一个空字符串。

如果声明了大小写无关的匹配,那么效果就好像把所有字母上的大小写区别 取消了一样。如果一个存在大小写差别的字母以一个普通字符的形式出现在方括弧 表达式外面,那么它实际上被转换成一个包含大小写的方括弧表达式,也就是说, x变成[xX]。如果它出现在一个方括弧表达式里面,那么它的所有大小写的同族都被加入 方括弧表达式中,也就是说,[x]变成[xX][^x]变成[^xX]

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

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

如果声明了反转换行敏感匹配,那么它影响^$,作用和换行敏感匹配里一样, 但是不影响.和方括号表达式。这个没什么太多用途,只是为了对称提供的。

9.7.3.6. 限制和兼容性

在这个实现里,对正则表达式的长度没有特别的限制,但是,那些希望能够有很好移植行的程序应该避免写超 过 256 字节的正则表达式 ,因为 POSIX 兼容的实现可以拒绝接受这样的正则表达式。

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

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

PostgreSQL 7.4 之前的版本里的 ARE 和 ERE 存在两个非常显然的不兼容:

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

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

9.7.3.7. 基本正则表达式

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