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

时间:2015-02-13 18:21:04

标签: sql regex oracle substring whitespace

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

我们使用的一个非常常见的分隔符是','因此,这是我的头号测试案例。我有不同的问题取决于我如何格式化正则表达式中的分隔符。

以下是我尝试的不同内容和结果:

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(例如马),结果就是' hor' ' E&#39 ;.

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

'[^(,\s)]+'
只要遇到逗号然后是空格,

就应该将字符串分开。但显然这不会发生。我还没有找到与我有类似问题的人。任何帮助将不胜感激

供参考我在SQL Developer上使用Oracle Database 11g企业版11.2.0.4.0版 - 64位生产

3 个答案:

答案 0 :(得分:3)

您对匹配字符列表的工作原理感到困惑。 From the documentation

  

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

     

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

     

范围运算符: -
  POSIX字符类:[::]   POSIX校对元素:[。 ]
  POSIX字符等价类:[= =]

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

据我所知,你不能否定一对价值观(虽然正则表达不是一个强点,所以我很有可能错了)。一种选择是用您知道不存在的字符替换分隔符的所有外观 - 根据您的实际数据,您可能必须选择一个不可打印的字符或一个模糊的Unicode字符 - 然后在正则表达式中使用它。 / p>

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

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                 

答案 1 :(得分: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,barthe cadleft

答案 2 :(得分:2)

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

三月通过一个字符串查找模式的多次出现'(.+?)(, |$)'

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

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

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

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