|
4.6. 模式匹配PostgreSQL 提供了两种实现模式匹配 的方法: SQL LIKE 操作符和 POSIX -风格正则表达式.
4.6.1. 用 LIKE 的模式匹配string LIKE pattern [ ESCAPE escape-character ] string NOT LIKE pattern [ ESCAPE escape-character ] 每个 pattern 定义一个字串的集合. 如果该 string 包含在 pattern 代表的字串集合里,那么 LIKE 表达式返回真. (和我们想象得一样,如果 LIKE 返回真,那么 NOT LIKE 表达式返回假, 反之亦然. 一个等效的表达式是 NOT ( string LIKE pattern ) .) 如果 pattern 不包含百分号 或者下划线,那么该模式只代表它本身; 这时候 LIKE 的行为就象等号操作符. 在 pattern 里的下划线 ( _ )代表(匹配)任何单个字符; 而一个百分号( % )匹配任何零或更多 字符长的字串. LIKE 模式匹配总是覆盖整个字串. 要匹配在字串内部任何位置的模式,该模式必须以百分号开头和结尾. 要匹配文本的下划线或者百分号,而不是匹配其它字符, 在 pattern 里相应的字符必须 前导逃逸字符.缺省的逃逸字符是反斜扛,但是你可以用 ESCAPE 子句指定一个. 要匹配逃逸字符本身,写两个逃逸字符. 请注意反斜扛在字串文本里已经有特殊含义了,所以如果你写一个 包含反斜扛的模式常量,那你就要在查询里写两个反斜扛. 因此,写一个匹配单个反斜扛的模式实际上要在查询里写四个反斜扛. 你可以通过用 ESCAPE 选择一个不同的逃逸字符 来避免这样;这样反斜扛就不再是 LIKE 的特殊字符了. 但仍然是字符文本分析器的特殊字符,索引你还是需要两个反斜扛.) 我们也可以通过写成 ESCAPE '' 的方式 选择不用逃逸字符.这时,我们就不能关闭下划线和百分号的特殊含义. 关键字 ILIKE 可以用于替换 LIKE , 令该匹配就当前的区域设置是大小写无关的. 这个特性不是 SQL 标准,是 PostgreSQL 扩展. 操作符 ~~ 等效于 LIKE , 而 ~~* 对应 ILIKE . 还有 !~~ 和 !~~* 操作符 分别代表 NOT LIKE 和 NOT ILIKE .所有这些操作符都是 PostgreSQL 特有的. 4.6.2. POSIX 正则表达式Table 4-10. 正则表达式匹配操作符
POSIX 正则表达式提供了比 LIKE 函数更强大的模式匹配的方法.许多 Unix 工具,比如 egrep , sed ,或 awk 使用一种与我们这里描述的类似的模式匹配语言. 正则表达式是一个字符序列,它是定义一个字串集合 (一个 正则集合 )的缩写. 如果一个字串是正则表达式描述的正则集合中的一员时, 我们就说这个字串匹配该正则表达式. 和 LIKE 一样,模式字符准确地匹配字串字符, 除非在正则表达式语言里有特殊字符 --- 不过正则表达式用的 特殊字符和 LIKE 用的不同. 和 LIKE 不一样的是,正则表达式 可以匹配字串里的任何位置,除非该正则表达式明确地挂接在字串 的开头或者结尾. 正则表达式( "RE" ),在 POSIX 1003.2 中定义, 它有两种形式:现代 RE(基本上就是那些在 egrep 里的; 1003.2 称它们为 "扩展" 的 RE)以及过时的 RE (基本上就是那些在 ed 里的; 1003.2 称它们为 "基本" 的 RE). PostgreSQL 实现了现代的形式. (现代)的 RE 是一个或多个非空的 分支 , 由 | 分隔.它匹配任何匹配其中一个分支的东西. 一个分支是一个或多个 片段 连接而成.一个片段匹配第一个,然后后面的片段匹配第二个, 等等. 一个片段是一个 原子 , 后面可能跟着一个 * , + , ? ,或者 范围 . 一个跟着 * 的原子匹配一个由零个或者更多个 匹配该原子的项组成的序列.一个跟着 + 的原子 匹配一个由一个或者更多匹配该原子的项组成的序列. 一个跟着 ? 的原子匹配一个由零个或者一个匹配该原子的项组成的序列. 范围 是 { 跟着一个无符号整数,可能还跟着 , 可能还跟着另外一个无符号整数,最后总是跟着一个 } . 这些整数介于 0 和 RE_DUP_MAX (255) (包含 RE_DUP_MAX ) 之间,并且如果有两个整数,第一个不能大于第二个. 一个后面跟着一个包含没有逗号的整数 i 的范围的原子匹配精确的 i 个原子. 一个后面跟着一个包含一个整数 i 和一个逗号的范围的原子匹配 i 个 或更多个该原子的序列. 一个后面跟着一个包含两个整数 i 和 j 的范围的原子匹配 i 到 j (包含 j )个该原子的序列.
原子 可以是:一个用 () 包围的正则表达式, (对应一个该正则表达式的匹配项), 一对空括弧 () (匹配空字串), 一个 方括弧表达式 (见下文), . (匹配任意单个字符), ^ (匹配一行开头的空字串), $ (匹配一行结尾的空字串), 一个后面跟着字符 ^.[$()|*+?{\ 的 \ (匹配把这些字符当做普通字符的字符), 或者一个没有其它标注的字符(匹配该字符本身). 一个 { 后面跟着一个非数字的字符就是 一个普通字符,而不是一个范围.用 \ 结束的 RE 是非法的. 请注意反斜扛( \ )在字串文本里已经有特殊的 含义了,所以如果你要写一个包含一个反斜扛的模式, 你必须在查询里写两个反斜扛. 方括弧表达式 是一个包围在 [] 里的字符列表.它通常匹配任意单个 列表中的字符(又见下文). 如果列表以 ^ 开头,它匹配 任意单个(又见下文)不在该列表中的字符. 如果该列表中两个字符用 - 隔开, 那它就是那两个字符(包括在内)之间的所有字符范围的缩写, 比如,在 ASCII 里 [0-9] 包含任何十进制数字. 两个范围共享一个终点是非法的,比如, a-c-e .这个范围与字符集关系密切, 可移植的程序不应该依靠它们. 想在列表中包含文本 ] ,可以让它做 列表的首字符(可能会在一个 ^ 后面). 想在列表中包含文本 - ,可以让它做 列表的首字符或者末字符,或者一个范围的第二个终点. 想在列表中把文本 - 当做范围的起点, 把它用 [. 和 .] 包围起来,这样它就成为一个集合元素(见下文). 除了一些这样的例外和一些用 [ 的组合(见下段)以外,所有其它特殊字符,包括 \ ,在方括弧表达式里都失去它们的特殊含义. 在一个方括弧表达式里,一个集合元素(一个字符,一个当做 一个字符的多字符序列,或者一个表示上面两种情况的集合序列) 包含在 [. 和 .] 里面的时候表示该集合元素的字符序列.该序列是该方括弧列表 的一个元素.因此一个包含多字符集合元素的方括弧表达式就 可以匹配多于一个字符,比如,如果集合序列包含一个 ch 集合元素, 那么 RE [[.ch.]]*c 匹配 chchcc 的头五个字符. (译注:其实把 [. 和 .] 括起来的当一个字符看就行了.) 在方括弧表达式里,在 [= 和 =] 里包围的集合元素是一个等效表, 代表等于这里所有集合元素的字符序列,包括它本身. (如果没有其它等效集合元素,那么就好象封装元素是 [. 和 .] .) 比如,如果 o 和 ^ 是一个等效表的成员,那么 [[=o=]] , [[=^=]] ,和 [o^] 都是同义的.一个等效表不能是一个范围的 端点. 在方括弧表达式里,在 [: 和 :] 里面封装的字符表名字代表 属于该表的所有字符的列表. 标准的字符表名字是: alnum , alpha , blank , cntrl , digit , graph , lower , print , punct , space , upper , xdigit . 它们代表在 ctype 里定义的字符表. 本地化设置可能会提供其他的表.字符表不能用做一个范围的端点. 在方括弧表达式里有两个特例:方括弧表达式 [[:<:]] 和 [[:>:]] 分别匹配一个单词开头和结束的空串. 单词定义为一个单词字符序列,前面和后面都没有其它单词字符. 单词字符是一个字母数字(和 ctype 里定义的一样) 或者一个下划线.这是一个扩展,兼容 POSIX 1003.2, 但那里面并没有说明, 而且在准备移植到其他系统里去的软件里一定要小心使用. 在一个 RE 可以匹配一个给定字串的多个子字串, RE 匹配那个在字串中最开头的那个.如果在该位置RE可以匹配多于 一个子字串,它匹配最长的.子表达式也是匹配最长的子字串, 由于有整个匹配越长越好的约束,所以靠前的子表达式的优先级比 靠后的高.请注意,高级的子表达式比低级的子表达式部件 的优先级高. 匹配长度是以字符计算的,而不是以集合元素. 空字串的长度被认为比什么都不匹配长. 比如, bb* 匹配 abbbc 的中间三个字符, (wee|week)(knights|nights) 匹配 weeknights 的所有十个字符, 而用 (.*).* 匹配 abc 的时候,圆括弧的子表达式匹配 所有三个字符,而如果用 (a*)* 匹配 bc ,那么整个 RE 和圆括弧的子表达式 都匹配空字串. 如果声明了大小写无关的匹配,其效果就好象在字符集里的 大小写区别都消失了一样.如果一个字母的大写和小写都出现在方括弧表达式外边 的普通字符里,那么你可以有效的把它们转换成一个包含大小写的 方括弧表达式,比如, x 变成 [xX] . 如果它出现在方括弧表达式里面,所有它相对的大小写都被加入到 方括弧表达式,因此(比如) [x] 变成 [xX] 而 [^x] 变成 [^xX] . 对 RE 的长度没有特别的限制,除了内存的限制以外. 内存的使用和 RE 的尺寸近似成线性关系, 并且随着 RE 的复杂度提高而极大增加,只有范围重复是例外. 范围重复是用宏扩展实现的,如果计数非常大或者范围重复是 嵌套的,那么开销就非常大.比如,象下面这样的 RE, ((((a{1,100}){1,100}){1,100}){1,100}){1,100} 会(最终)用光几乎任何现有机器的的交换空间. [1] Notes
|