正则表达式:如何在 PL/SQL 中实现负向后查找
Posted
技术标签:
【中文标题】正则表达式:如何在 PL/SQL 中实现负向后查找【英文标题】:Regex: How to Implement Negative Lookbehind in PL/SQL 【发布时间】:2014-11-12 21:34:23 【问题描述】:如何匹配所有以loockup.
开头并以_id
结尾但不以msg
为前缀的字符串?下面是一些例子:
lookup.asset_id -> should match
lookup.msg_id -> shouldn't match
lookup.whateverelse_id -> should match
我知道 Oracle 不支持负向回溯(即(?<!)
)...所以我尝试使用交替显式枚举可能性:
regexp_count('i_asset := lookup.asset_id;', 'lookup\.[^\(]+([^m]|m[^s]|ms[^g])_id') <> 0 then
dbms_output.put_line('match'); -- this matches as expected
end if;
regexp_count('i_msg := lookup.msg_id;', 'lookup\.[^\(]+([^m]|m[^s]|ms[^g])_id') <> 0 then
dbms_output.put_line('match'); -- this shouldn’t match
-- but it does like the previous example... why?
end if;
第二个regexp_count
表达式不应该匹配...但它确实像第一个。我错过了什么吗?
编辑
在实际用例中,我有一个包含 PL/SQL 代码的字符串,其中可能包含多个 lookup.xxx_id
实例:
declare
l_source_code varchar2(2048) := '
...
curry := lookup.curry_id(key_val => ''CHF'', key_type => ''asset_iso'');
asset : = lookup.asset_id(key_val => ''UBSN''); -- this is wrong since it does
-- not specify key_type
...
msg := lookup.msg_id(key_val => ''hello''); -- this is fine since msg_id does
-- not require key_type
';
...
end;
我需要确定是否至少有一个错误的lookup
,即除了lookup.msg_id
之外的所有出现,还必须指定key_type
参数。
【问题讨论】:
是单个字符串还是多个字符串的串联?可以有很多级别,例如 'lookup.tunnel.east.msg_id' 'lookup.tunnel.east.alternative_msg_id' 呢?有很多方法可以做到这一点,但需要更多信息。 不,只有一层。始终为lookup.xyz_id(param1, param2, paramN)
。
【参考方案1】:
使用lookup\.[^\(]+([^m]|m[^s]|ms[^g])_id
,您基本上是在要求检查字符串
-
以
lookup.
开头,用lookup\.
表示,
后跟至少一个不同于(
的字符,用[^\(]+
表示,
后跟 -- ( | | )
一个不同于m
的字符--[^m]
,或者
两个字符:m
加上没有s
-- m[^s]
,或
三个字符:ms
,没有g
——ms[^g]
,和
以_id
结尾,用_id
表示。
因此,对于lookup.msg_id
,第一部分显然匹配,第二部分消耗ms
,而将g
留给第三部分的第一个替代项。
这可以通过将第三部分修补为始终为三个字符长(如lookup\.[^\(]+([^m]..|m[^s.]|ms[^g])_id
)来解决。然而,这会导致一切都失败,lookup.
和 _id
之间的部分长度至少为四个字符:
WITH
Input (s, r) AS (
SELECT 'lookup.asset_id', 'should match' FROM DUAL UNION ALL
SELECT 'lookup.msg_id', 'shouldn''t match' FROM DUAL UNION ALL
SELECT 'lookup.whateverelse_id', 'should match' FROM DUAL UNION ALL
SELECT 'lookup.a_id', 'should match' FROM DUAL UNION ALL
SELECT 'lookup.ab_id', 'should match' FROM DUAL UNION ALL
SELECT 'lookup.abc_id', 'should match' FROM DUAL
)
SELECT
r, s, INSTR(s, 'lookup.msg_id') has_msg, REGEXP_COUNT(s , 'lookup\.[^\(]+([^m]..|m[^s]|ms[^g])_id') matched FROM Input
;
| R | S | HAS_MSG | MATCHED |
|-----------------|------------------------|---------|---------|
| should match | lookup.asset_id | 0 | 1 |
| shouldn't match | lookup.msg_id | 1 | 0 |
| should match | lookup.whateverelse_id | 0 | 1 |
| should match | lookup.a_id | 0 | 0 |
| should match | lookup.ab_id | 0 | 0 |
| should match | lookup.abc_id | 0 | 0 |
如果您只是要确保在相关职位中没有msg
,您可能想要
(INSTR(s, 'lookup.msg_id') = 0) AND REGEXP_COUNT(s, 'lookup\.[^\(]+_id') <> 0
为了代码清晰,REGEXP_INSTR(s, 'lookup\.[^\(]+_id') > 0
可能更可取……
@j3d 如果需要更多详细信息,请发表评论。
【讨论】:
感谢您的好评。如果我在同一个字符串中出现更多lookup.xxx_id
怎么办?在现实世界中,我需要检查可能包含任意数量的匹配或不匹配 lookup
实例的源代码。
@j3d 你能提供更多细节吗?特别是:除了lookup.msg_id
,还有更多的“黑名单”令牌吗?如果字符串同时包含要匹配和不匹配的标记,那么预期的响应是什么?检查的更大目的是什么?
@j3d 我说得对吗:(i) 最初,您想挑选出msg
令牌以便不应用keytype
check? (ii) 您需要检查语义而不是语法合规性 - 换句话说:例如,不会缺少引号,但 key_type
参数可能会丢失? (iii) 对要遵守的值的任何其他限制(大小写、特殊字符(不允许)等)?
只有在lookup.
后跟msg_id
时,才可能缺少key_type
参数。对于任何其他情况,必须指定 key_type
参数(或者必须指定至少两个参数)。
@j3d 我猜,您的更新中asset : =
中的第二个空白有误?【参考方案2】:
要求仍然有点模糊......
-
在分号处拆分字符串。
检查每个子字符串s
是否符合:
WITH Input (s) AS (
SELECT ' curry := lookup.curry_id(key_val => ''CHF'', key_type => ''asset_iso'');' FROM DUAL UNION ALL
SELECT 'curry := lookup.curry_id(key_val => ''CHF'', key_type => ''asset_iso'');' FROM DUAL UNION ALL
SELECT 'asset := lookup.asset_id(key_val => ''UBSN'');' FROM DUAL UNION ALL
SELECT 'msg := lookup.msg_id(key_val => ''hello'');' FROM DUAL
)
SELECT
s
FROM Input
WHERE REGEXP_LIKE(s, '^\s*[a-z]+\s+:=\s+lookup\.msg_id\(key_val => ''[a-zA-Z0-9]+''\);$')
OR
((REGEXP_INSTR(s, '^\s*[a-z]+\s+:=\s+lookup\.msg_id') = 0)
AND (REGEXP_INSTR(s, '[(,]\s*key_type') > 0)
AND (REGEXP_INSTR(s,
'^\s*[a-z]+\s+:=\s+lookup\.[a-z]+_id\(( ?key_[a-z]+ => ''[a-zA-Z_]+?'',?)+\);$') > 0))
;
| S |
|--------------------------------------------------------------------------|
|[tab] curry := lookup.curry_id(key_val => 'CHF', key_type => 'asset_iso');|
| curry := lookup.curry_id(key_val => 'CHF', key_type => 'asset_iso');|
| msg := lookup.msg_id(key_val => 'hello');|
这将允许右括号之前的多余逗号。但是如果输入在语法上是正确的,那么这样的逗号就不会存在。
【讨论】:
以上是关于正则表达式:如何在 PL/SQL 中实现负向后查找的主要内容,如果未能解决你的问题,请参考以下文章
在 oracle 中使用正则表达式查找 POBOX - PL/SQL