Postgres / TCL风格正则表达式:使用环顾四周来取消匹配

时间:2019-01-23 08:33:50

标签: regex postgresql regex-lookarounds

我有一个查询,我想在其中匹配特定的子字符串。例如,让我们将所有条目与单词“ rice”匹配:

# select name from menus where name ~* 'rice' order by price limit 3;
         name
----------------------
 Rice Veges
 1/2 Rice 1/2 Githeri
 1/2 Rice 1/2 Chapo
(3 rows)

更具体的匹配也可以。请注意,加上1/2

 select name from menus where name ~* '(1/2) rice' order by price limit 3;
         name
----------------------
 1/2 Rice 1/2 Githeri
 1/2 Rice 1/2 Chapo
 1/2 Rice 1/2 Matoke
(3 rows)

因此,假设我要选择所有大米,但我不希望其中包含1/2。

本质上,我要Set(Rice)-Set(1/2 Rice)。我在这里的攻击角度是四处张望,将被否定。

# select name from menus where name ~* '(?!1/2) rice' order by price limit 3;
         name
----------------------
 1/2 Rice 1/2 Chapo
 1/2 Rice 1/2 Githeri
 1/2 Rice 1/2 Matoke


您可以看到上面的表达式无法工作,因为它仍然与子字符串1/2匹配

# select name from menus where name ~* '(?!2)\s*rice' order by price limit 3;
         name
----------------------
 Rice Veges
 1/2 Rice 1/2 Githeri
 1/2 Rice 1/2 Chapo

简化表达式以删除可能由于未正确转义的字符串而引起问题的'1 /'并不能产生我们想要的结果。

我们可以确认是否支持否定环视:

# select name from menus where name ~* '(?!r)ice' order by price limit 3;
      name
-----------------
 Juice Container
 Mango Juice
 Passion Juice

这与任何具有'ice'但不带有'r'的字符串匹配。

1 个答案:

答案 0 :(得分:1)

请注意,(?!r)ice = icei不等于r(?!r)是一个否定的 lookahead ,如果其模式将文本立即匹配到当前位置的 right ,则匹配失败。

您实际上想使用'(?<!1/2 )rice',其中(?<!1/2 )是一个负向后看,如果其模式与当前位置左侧的文本相匹配,则匹配失败。

PostgreSQL demo

CREATE TABLE tb1
    (s character varying)
;

INSERT INTO tb1
    (s)
VALUES
    ('Rice Veges'),
    ('1/2 Rice 1/2 Githeri')
;

SELECT * FROM tb1 WHERE s ~* '(?<!1/2 )rice';

结果:

enter image description here

要将1作为一个整体匹配,请添加一个\y词边界'(?<!\y1/2 )rice'(例如,如果要返回11/2 Rice 1/2 Githeri)。