跳转至

模式匹配

模式匹配

SeaboxSQL提供了三种独立的实现模式匹配的方法:SQL LIKE操作符、更近一些的SIMILAR TO操作符(SQL:1999 里添加进来的)和POSIX-风格的正则表达式。

除了这些基本的“串匹配模式”操作符外,还有一些函数可用于提取或替换匹配子串并在匹配位置分离一个串。

注意

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

  • 虽然大部分的正则表达式搜索都能被很快地执行,但是正则表达式仍可能被人为地弄成需要任意长的时间和任意量的内存进行处理。要当心从不怀好意的来源接受正则表达式搜索模式。如果必须这样做,建议加上语句超时限制。

  • 使用SIMILAR TO模式的搜索具有同样的安全性危险, 因为SIMILAR TO提供了很多和 POSIX-风格正则表达式相同的能力。

  • LIKE搜索比其他两种选项简单得多,因此在使用不怀好意的模式来源时要更安全些。

LIKE/NOT LIKE

语法
sql 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里的下划线(_)代表(匹配)任何单个字符; 而一个百分号(%)匹配任何零或更多个字符的序列。

示例

``` sql seaboxsql=# select 'abc' LIKE 'abc'; ?column?


t (1 row)

seaboxsql=# select 'abc' LIKE 'a%' ; ?column?


t (1 row)

seaboxsql=# select 'abc' LIKE 'b' ; ?column?


t (1 row)

seaboxsql=# select 'abc' LIKE 'c'; ?column?


f (1 row) ```

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

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

注意

  • 如果关掉了GUC配置参赛standard_conforming_strings,在文串常量中写的任何反斜线都需要被双写。

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

也可以通过写ESCAPE ''的方式不选择转义字符,这样可以有效地禁用转义机制,但是没有办法关闭下划线和百分号在模式中的特殊含义。

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

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

  • 在仅需要从字符串的开始部分搜索的情况,还有前缀操作符^@和相应的starts_with函数可以使用。

SIMILAR TO正则表达式

语法
sql string SIMILAR TO pattern [ESCAPE escape-character] string NOT SIMILAR TO pattern [ESCAPE escape-character]
描述

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

类似LIKESIMILAR TO操作符只有在它的模式匹配整个串的时候才能成功;这一点和普通的正则表达式的行为不同,在普通的正则表达式里,模式匹配串的任意部分。

LIKE类似的地方还有,SIMILAR TO使用_%作为分别代表任意单个字符和任意串的通配符(这些可以比得上 POSIX正则表达式里的..*)。

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

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

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

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

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

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

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

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

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

  • 一个方括号表达式[...]声明一个字符类,就像 POSIX 正则表达式一样。

注意点号(.)不是SIMILAR TO的一个元字符。

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

示例

``` sql seaboxsql=# select 'abc' SIMILAR TO 'abc'; ?column?


t (1 row)

seaboxsql=# select 'abc' SIMILAR TO 'a' ; ?column?


f (1 row)

seaboxsql=# select 'abc' SIMILAR TO '%(b|d)%'; ?column?


t (1 row)

seaboxsql=# select 'abc' SIMILAR TO '(b|c)%'; ?column?


f (1 row) ```

POSIX正则表达式

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

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

以下是所有可用于POSIX 正则表达式模式匹配的操作符和函数的说明。

~
描述
匹配正则表达式,大小写敏感
示例

``` sql seaboxsql=# select 'thomas' ~ '.thomas.'; ?column?


t (1 row) seaboxsql=# select 'abc' ~ 'abc'; ?column?
t (1 row)

seaboxsql=# select 'abc' ~ '^a' ; ?column?


t (1 row)

seaboxsql=# select 'abc' ~ '(b|d)'; ?column?


t (1 row)

seaboxsql=# select 'abc' ~ '^(b|c)'; ?column?


f (1 row) ```

~*
描述
匹配正则表达式,大小写不敏感
示例
``` sql seaboxsql=# select 'thomas' ~* '.Thomas.'; ?column?
t (1 row) ```
!~
描述
不匹配正则表达式,大小写敏感
示例
``` sql seaboxsql=# select 'thomas' !~ '.Thomas.'; ?column?
t (1 row) ```
!~*
描述
不匹配正则表达式,大小写不敏感
示例
``` sql seaboxsql=# select 'thomas' !~* '.vadim.'; ?column?
t (1 row) ```
substring()
语法
substring(string from pattern [ for escape-character])
描述

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

带三个参数的substring,增加了指定转义字符的功能。和SIMILAR TO一样,声明的模式必须匹配整个数据串,否则函数失败并返回空值。为了标识在成功的时候应该返回的模式部分,模式必须包含转义字符的两次出现,并且后面要跟上双引号(")。匹配这两个标记之间的模式的文本将被返回。

示例

``` sql seaboxsql=# select substring('foobar' from 'o.b'); substring


oob (1 row)

seaboxsql=# select substring('foobar' from 'o(.)b'); substring


o (1 row)

一些使用`#"`定界返回串的示例如下:

``` sql
seaboxsql=# select substring('foobar' from '%#"o_b#"%' for '#') ;
 substring 
-----------
 oob
(1 row)

seaboxsql=# select substring('foobar' from '#"o_b#"%' for '#') ;
 substring 
-----------
 
(1 row)

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

``` sql seaboxsql=# select regexp_replace('foobarbaz', 'b..', 'X'); regexp_replace


fooXbaz (1 row)

seaboxsql=# select regexp_replace('foobarbaz', 'b..', 'X', 'g'); regexp_replace


fooXX (1 row)

seaboxsql=# select regexp_replace('foobarbaz', 'b(..)', 'X1Y', 'g'); regexp_replace


fooXarYXazY (1 row) ```

regexp_match()
语法
regexp_match(string,pattern [, flags])
描述
返回一个文本数组,它包含一个POSIX正则表达式模式与一个字符串第一个匹配所得到的子串。如果没有匹配,则结果为NULL。如果找到一个匹配并且pattern不包含带括号的子表达式,那么结果是一个单一元素的文本数组,其中包含匹配整个模式的子串。如果找到一个匹配并且pattern含有带括号的子表达式,那么结果是一个文本数组,其中第n个元素是与pattern的第n个圆括号子表达式匹配的子串(“非捕获”圆括号不计入在内,详见下文)。flags参数是一个可选的文本字符串,它包含零个或者更多个可以改变该函数行为的单字母标志。支持的标志在下表“ARE 嵌入选项字母“中描述。
示例

``` sql seaboxsql=# SELECT regexp_match('foobarbequebaz', 'bar.*que'); regexp_match


{barbeque} (1 row)

seaboxsql=# SELECT regexp_match('foobarbequebaz', '(bar)(beque)'); regexp_match


{bar,beque} (1 row)

在通常情况下,只是想要的大整个匹配的子串或者`NULL`(没有匹配),可以写成这样

``` sql
seaboxsql=# SELECT (regexp_match('foobarbequebaz', 'bar.*que'))[1];
 regexp_match
--------------
 barbeque
(1 row)

regexp_matches()
语法
regexp_matches(string,pattern [, flags])
描述
regexp_matches函数返回一个文本数组的集合,其中包含着一个POSIX正则表达式模式与一个字符串匹配得到的子串。如果没有匹配,这个函数不会返回行。如果有一个匹配并且给定了g标志,则返回一行。如果有N个匹配并且给定了g标志,则返回N行。每一个返回的行都是一个文本数组,其中含有整个匹配的子串或者匹配pattern的圆括号子表达式的子串,这和上面对regexp_match的介绍一样。regexp_matches接受"表 ARE 嵌入选项字母"中展示的所有标志,外加令它返回所有匹配而不仅仅是第一个匹配的g标志。
示例

``` sql seaboxsql=# SELECT regexp_matches('foo', 'not there'); regexp_matches


(0 rows)

seaboxsql=# SELECT regexp_matches('foobarbequebazilbarfbonk', '(b[b]+)(b[b]+)', 'g'); regexp_matches


{bar,beque} {bazil,barf} (2 rows) ```

注意
在大部分情况下,regexp_matches()应该与g标志一起使用,因为如果只是想要第一个匹配,使用regexp_match()会更加简单高效。
regexp_split_to_table()
语法
regexp_split_to_table(string,pattern [, flags])
描述
regexp_split_to_table把一个 POSIX正则表达式模式当作一个定界符来分离一个串。如果没有与pattern的匹配,该函数返回string。如果有至少有一个匹配,对每一个匹配它都返回从上一个匹配的末尾(或者串的开头)到这次匹配开头之间的文本。当没有更多匹配时,它返回从上一次匹配的末尾到串末尾之间的文本。flags参数是一个可选的文本串,它包含零个或更多单字母标志,这些标识可以改变该函数的行为。regexp_split_to_table能支持的标志在"表 ARE 嵌入选项字母"中描述。
示例
``` sql seaboxsql=# 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) seaboxsql=# 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_to_array()
语法
regexp_split_to_array(string,pattern [, flags])
描述
regexp_split_to_array函数的行为和regexp_split_to_table相同,不过regexp_split_to_array会把它的结果以一个text数组的形式返回。
示例
``` sql seaboxsql=# 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) ```

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

正则表达式细节

SeaboxSQL正则表达式与PostgreSQL的正则表达式一样, 是使用 Henry Spencer写的一个包来实现的。以下正则表达式的大部分描述都是从 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 的区别是什么。

正则表达式元语法

SeaboxSQL数据库默认总是推测一个正则表达式遵循 ARE规则。但可以通过为 RE 模式预置一个embedded option来选择限制更多的 ERE 或BRE规则,如正则表达式元语法中所述。

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

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

  • 一个量化原子是一个原子, 后面可能跟着一个量词。没有量词的时候,它匹配一个原子,有量词的时候,它可以匹配若干个原子。一个原子可以是在下文显示的任何可能。

以下是:

(re)
(其中re是任何正则表达式) 匹配一个对re的匹配,匹配将为可能的报告被记下
(?:re)
同上,但是匹配不会为了报告而被记下 (一个“非捕获”圆括号集) (只对 ARE)
.
匹配任意单个字符
[chars]
一个方括号表达式, 匹配chars中的任意一个
\k
(其中k是一个非字母数字字符) 匹配一个被当作普通字符看待的特定字符, 例如,\\匹配一个反斜线字符
\c
其中c是一个字母数字 (可能跟着其它字符),它是一个转义,(仅对 ARE; 在 ERE 和 BRE 中,它匹配c
{
如果后面跟着一个字符,而不是数字, 那么就匹配左花括弧{;如果跟着一个数字, 那么它是range的开始(见下文)
x
其中x是一个没有其它意义的单个字符,则匹配该字符。

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

注意

  • 如果关掉了GUC配置参数standard_conforming_strings,任何写在文字串常量中的反斜线都需要被双写。
正则表达式量词

正则表达式量词及其匹配含义见下表

表 正则表达式量词

量词 匹配
* 一个由原子的 0 次或更多次匹配组成的序列
+ 一个由原子的 1 次或更多次匹配组成的序列
? 一个由原子的 0 次或 1 次匹配组成的序列
{m} 一个由原子的正好m次匹配组成的序列
{m,} 一个由原子的m次或更多次匹配组成的序列
{m,n} 一个由原子的从m次到n次(包括)匹配组成的序列;m不能超过n
*? *的非贪婪版本
+? +的非贪婪版本
?? ?的非贪婪版本
{m}? {m}的非贪婪版本
{m,}? {m,}的非贪婪版本
{m,n}? {m,n}的非贪婪版本

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

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

注意

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

一个约束匹配一个空串,但只是在满足特定条件下才匹配。

约束可以在能够使用原子的地方使用,只是它不能跟着量词。简单的约束在下表里显示:

表 正则表达式约束

约束 描述
^ 串开头的匹配
$ 串末尾的匹配
(?=re) 在匹配re的子串开始的任何点的* positive lookahead*匹配(只对 ARE)
(?!re) 在匹配re的子串开始的任何点的* negative lookahead*匹配(只对 ARE)
(?<=re) 只要有一个点上有一个子串匹配re端, positive lookbehind 就在这个点上匹配(只对 ARE)
(?<!--re) 只要有一个点上没有子串匹配re端, negative lookbehind 就在这个点上匹配(只对 ARE)

lookahead 和 lookbehind 约束不能包含后引用,并且其中的所有圆括号都被认为是非捕获的。

方括号表达式

方括号表达式是一个包围在[]中的字符列表。它通常匹配列表中的任意单个字符。

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

  • 想在列表中包含文本],可以让它做列表的首字符(如果使用了^,需要放在其后)。

  • 想在列表中包含文本-,可以让它做列表的首字符或者尾字符,或者一个范围的第二个端点。

  • 想在列表中把文本-当做范围的起点, 把它用[..]包围起来,这样它就成为一个排序元素。

除了这些字符本身、一些用[的组合(见下段)以及转义(只在 ARE中有效)以外,所有其它特殊字符 在方括号表达式里都失去它们的特殊含义。特别是,在 ERE 和 BRE规则下\不是特殊的, 但在 ARE 里,它是特殊的(引入一个转义)。

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

注意

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

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

在方括号表达式里,在[::]里面封装的字符类的名字代表属于该类的所有字符的列表。 标准的字符类名字是:alnumalphablankcntrldigitgraphlowerprintpunctspaceupperxdigit。它们代表在ctype中定义的字符类。

一个区域可以会提供其他的类。字符类不能用做一个范围的端点。

方括号表达式里有两个特例:方括号表达式[[:<:]][[:-->:]]是约束,分别匹配一个单词开头和结束的空串。单词定义为一个单词字符序列,前面和后面都没有其它单词字符。单词字符是一个alnum字符(和ctype中定义的一样)或者一个下划线。

正则表达式转义

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

字符项转义用于便于我们在 RE中声明那些不可打印的或其他习惯的字符。如下表"表 正则表达式字符项转义"中内容。

类缩写转义用来提供一些常用的字符类缩写。如下表 "表 正则表达式类缩写转义"中内容。

约束转义是一个约束,如果满足特定的条件,它匹配该空串。如下表 "表 正则表达式约束转义"中内容。

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

表 正则表达式字符项转义

转义 描述
\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-fA-F。八进制位是0-7

指定 ASCII 范围(0-127)之外的值的数字字符项转义的含义取决于数据库编码。 当编码是 UTF-8 时,转义值等价于 Unicode代码点,例如 \u1234表示字符U+1234。对于其他多字节编码,字符项转义通常只是指定该字符的字节值的串接。如果该转义值不对应数据库编码中的任何合法字符,将不会发生错误,但是它不会匹配任何数据。

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

表 正则表达式类缩写转义

转义 描述
\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:]]的,也是非法的)。

表 正则表达式约束转义

转义 描述
\A 只在串开头匹配(与^不同)
\m 只在一个词的开头匹配
\M 只在一个词的末尾匹配
\y 只在一个词的开头或末尾匹配
\Y 只在一个词的不是开头或末尾的点上匹配
\Z 只在串的末尾匹配(与$不同)

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

表 正则表达式后引用

转义 描述
\m (其中m是一个非零位)一个到第m个子表达式的后引用
\mnn (其中m是一个非零位, 并且nn是一些更多的位, 并且十六进制值 mnn 不超过目前能看到的封闭捕获圆括号的数目) 一个到第mnn个子表达式的后引用

注意

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

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

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

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

表 ARE 嵌入选项字母

选项 描述
b RE的剩余部分是一个BRE
c 大小写敏感的匹配(覆盖操作符类型)
e RE的剩余部分是一个ERE
i 大小写不敏感的匹配(覆盖操作符类型)
m n的历史原因的同义词
n 新行敏感的匹配
p 部分新行敏感的匹配)
q RE的剩余部分是一个文字(“quoted”)串,全部是普通字符
s 非新行敏感的匹配(默认)
t 紧语法(默认,见下文)
w 逆部分新行敏感(“怪异”)的匹配
x 扩展语法(见下文)

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

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

  • 空白字符或前置了\#将被保留

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

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

为了这个目的,空白是空格、制表符、新行和任何属于空白字符类的字符。

最后,在 ARE 里,方括号表达式外面,序列(?#ttt)(其中ttt是任意不包含一个))的文本)是一个注释,它被完全忽略。同样,这样的东西是不允许出现在多字符符号的字符中间的,例如(?:。这种注释更像是一种历史产物而不是一种有用的设施,并且它们的使用已经被废弃;请使用扩展语法来替代。

如果声明了一个初始的***=控制器,那么所有这些元语法扩展都不能使用,因为这样表示把用户输入当作一个文字串而不是RE 对待。

正则表达式匹配规则

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

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

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

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

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

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

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

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

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

上面的规则所描述的贪婪属性不仅仅适用于独立的量化原子, 而且也适用于包含量化原子的分支和整个 RE。这里的意思是, 匹配是按照分支或者整个 RE作为一个整体匹配最长或者最短的可能子串。

一旦整个匹配的长度确定,那么匹配任意特定子表达式的部分就基于该子表达式的贪婪属性进行判断,在RE 里面靠前的子表达式的优先级高于靠后的子表达式。

一个相应的例子:

seaboxsql=# SELECT SUBSTRING('XY1234Z', 'Y*([0-9]{1,3})');
 substring 
-----------
 123
(1 row)

seaboxsql=# SELECT SUBSTRING('XY1234Z', 'Y*?([0-9]{1,3})');
 substring 
-----------
 1
(1 row)

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

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

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

seaboxsql=# SELECT regexp_matches('abc01234xyz', '(.*)(\d+)(.*)');
 regexp_matches  
-----------------
 {abc0123,4,xyz}
(1 row)

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

seaboxsql=# SELECT regexp_matches('abc01234xyz', '(.*?)(\d+)(.*)');
 regexp_matches 
----------------
 {abc,0,""}
(1 row)

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

seaboxsql=# SELECT regexp_matches('abc01234xyz', '(?:(.*?)(\d+)(.*)){1,1}');
 regexp_matches  
-----------------
 {abc,01234,xyz}
(1 row)

独立于 RE 的组件的贪婪性之外控制 RE 的整体贪婪性为处理变长模式提供了 很大的灵活性。

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

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

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

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

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

限制和兼容性说明

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

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

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

基本正则表达式

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