SQL中的正则表达式子字符串在两个字符分隔符上

Posted

技术标签:

【中文标题】SQL中的正则表达式子字符串在两个字符分隔符上【英文标题】:Regular Expression Substring in SQL on two character delimeter 【发布时间】:2015-04-14 21:09:33 【问题描述】:

我正在尝试获取一个分隔字符串并返回分隔符之间的每个子字符串。这用于我正在编写的更大函数中,因此分隔符通常是一个变量。

我们使用的一个非常常见的分隔符是“,”,因此这是我的第一个测试用例。根据我在正则表达式中格式化分隔符的方式,我遇到了不同的问题。

以下是我尝试过的不同方法和结果:

select REGEXP_SUBSTR ('foo bar', '[^' || '(, )' || ']+', 1, LEVEL) item
        from dual
        connect by REGEXP_SUBSTR ('foo bar', '[^' || '(, )' || ']+', 1, LEVEL

select REGEXP_SUBSTR ('foo bar', '[^' || '(,\s)' || ']+', 1, LEVEL) item
        from dual
        connect by REGEXP_SUBSTR ('foo bar', '[^' || '(,\s)' || ']+', 1, LEVEL

select REGEXP_SUBSTR ('foo bar', '[^' || '(,[:blank:])' || ']+', 1, LEVEL) item
        from dual
        connect by REGEXP_SUBSTR ('foo bar', '[^' || '(,[:blank:])' || ']+', 1, LEVEL

第一次和第三次尝试将空格上的 'foo' 和 'bar' 分开,即使没有逗号。后一种尝试按希望将 'foo' 和 'bar' 保持在同一行,但如果字符串中有一个 s(例如 horse),则结果是 'hor' 'e'。

我对正则表达式和 regexp_substr 的理解告诉我

'[^(,\s)]+'

应该在遇到逗号和空格时分隔字符串。但显然这并没有发生。我还没有找到和我有类似问题的人。任何帮助将不胜感激

作为参考,我在 SQL Developer 中使用 Oracle Database 11g Enterprise Edition Release 11.2.0.4.0 - 64bit Production

【问题讨论】:

我不太明白你的问题,你为什么不改用[^,[:blank:]]+ @Aramillo - 会在逗号 空格上分割,而不是两个相邻的字符对。 OP 使用括号将它们视为一个单元在匹配字符列表中不起作用。 @AlexPoole 啊,现在我明白了。谢谢!! 【参考方案1】:

使用利用非贪婪量词的文本模式

遍历一个字符串以查找该模式的多次出现,'(.+?)(, |$)'

模式(.+?) 是一个字符组。 . 指代任何/所有字符,+? 是 1 个或多个字符的非贪婪量词。

模式(, |$) 查找出现', ' 或(交替运算符,|)字符串结尾$。这是第二个字符组。

最后,使用子表达式仅引用第一个字符组

SCOTT@dev> VAR tval VARCHAR2(500);
SCOTT@dev> EXECUTE :tval := 'foo,bar, great';

PL/SQL procedure successfully completed.

SCOTT@dev> SELECT regexp_substr(:tval,'(.+?)(, |$)', 1, LEVEL, NULL, 1) t_val
  2  FROM dual
  3    CONNECT BY regexp_substr(:tval,'(.+?)(, |$)', 1, LEVEL, NULL, 1) IS NOT NULL
  4  /

T_VAL
--------
foo,bar
great

SCOTT@dev> VAR tval VARCHAR2(500);
SCOTT@dev> EXECUTE :tval := 'foo, bar, great';

PL/SQL procedure successfully completed.

SCOTT@dev> /

T_VAL
--------
foo
bar
great

SCOTT@dev> VAR tval VARCHAR2(500);
SCOTT@dev> EXECUTE :tval := 'foo,bar,great';

PL/SQL procedure successfully completed.

SCOTT@dev> /

T_VAL
--------
foo,bar,great

SCOTT@dev> VAR tval VARCHAR2(500);
SCOTT@dev> EXECUTE :tval := ',foo, bar, great';

PL/SQL procedure successfully completed.

SCOTT@dev> /

T_VAL
--------
,foo
bar
great

【讨论】:

【参考方案2】:

你也可以这样做:

select trim(regexp_substr('foo bar, the cad, left','[^,]+',1,level)) from dual
connect by level <= regexp_count('foo bar, the cad, left',',')+1;

此查询搜索 , 而不是 ,(space)。但我认为你会得到相同的结果。如果您有 foo,bar, the cad, left 并期望,这不起作用 foo,bar,the cad,left

【讨论】:

只匹配一个逗号,以及一个逗号后跟一个空格。不过,这可能对这里的 OP 有用。 (取决于我认为不太常见的分隔符)。 感谢@AlexPoole,我在您发表评论时正在编辑。:) 修剪是一种有趣的方式,谢谢你,我只选择了另一个选项,因为它似乎更通用 是的,你做得对,我认为@AlexPoole 的回答更笼统。【参考方案3】:

您对匹配字符列表的工作方式感到困惑。 From the documentation:

[char...] 匹配字符列表

匹配括号内列表中的任何单个字符。在列表中,除这些之外的所有 > 运算符都被视为文字:

范围运算符:- POSIX 字符类:[: :] POSIX 排序规则元素:[. .] POSIX 字符等价类:[= =]

因此,在您的模式'[^(,\s)]+' 中,每个字符都被视为文字; \ 不会使s 被视为空白字符,它只是一个s,因此它在horse 中匹配。并且括号也是文字,因此它们不会将这对字符包含在分隔符中,每个字符都只匹配字符串中的实际括号。在您的第一次和第三次尝试中,您只在一个空格上得到了匹配,因为匹配列表中的每个字符都是独立的,它们不会像您期望的那样通过括号组合。

据我所知,您不能否定一对值(尽管正则表达式不是一个强项,所以我很有可能错了)。一种选择是用您知道不会出现的字符替换所有出现的定界符 - 根据您的实际数据,您可能必须选择一个不可打印的字符或一个晦涩的 Unicode 字符 - 然后在正则表达式中使用它。

例如,为了简洁起见,使用绑定变量和哈希作为我知道不存在的字符:

variable string varchar2(20);
variable delimiter varchar2(2);

exec :string := 'foo bar, the cad, left';
exec :delimiter := ', ';

select regexp_substr(replace(:string, :delimiter, '#'),
  '[^#]+', 1, level) as item
from dual
connect by regexp_substr(replace(:string, :delimiter, '#'),
  '[^#]+', 1, level) is not null;

ITEM                
--------------------
foo bar              
the cad              
left                 

【讨论】:

感谢您教我匹配字符列表。我只是想在普通的正则表达式中,'(abc)*' 将如何匹配 abcabc 而不是 aaa。 Oracle SQL 的正则表达式在使用中似乎有点不稳定,但这既不是这里也不是那里。而且您的建议非常有效,我不相信我们会在使用此功能的字段中使用哈希值,我可以将其包含在文档中,如果有问题,请采纳您的建议并寻求非可打印字符。再次感谢!

以上是关于SQL中的正则表达式子字符串在两个字符分隔符上的主要内容,如果未能解决你的问题,请参考以下文章

初步学习正则表达式

如何替换 Javascript 中的正则表达式子字符串匹配?

mysql 中用正则表达式如何取一个字符串中指定的字段,

简单的js正则表达式问题

正则表达式量词 - 两个字符之间的数量

C# 正则表达式子字符串应该在开始和结束但不在中间