oracle中regexp_replace的不同值不起作用

时间:2015-10-27 14:36:38

标签: sql regex oracle oracle11g

我试图从管道分隔文本中提取不同的值。当我在谷歌搜索时,我得到了以下表达,但在某些情况下它不起作用

EG:

   select regexp_replace('Bhal|Bhal|Bhal|Bhaloo|Bhaloo|Bhaloo|Bhaloooo|Bhaloooo|Bhaloooo|Baker|Baker|Baker', '([^|]+)(\|\1)+', '\1') from dual;

预期产出:

  

Bhal | Bhaloo | Bhaloooo |贝克

我在正则表达式中尝试了一些组合,但它对我不起作用。

任何帮助都将不胜感激。

5 个答案:

答案 0 :(得分:1)

这肯定是一个挑战。首先要了解为什么原件失败了。找到'Bhal'的第一个字符串也是第二个字符串'Bhaloo'的第一部分。因此,字符串的一部分与'([^|]+)(\|\1)+'的原始正则表达式相匹配(读作:匹配一组一个或多个不是管道的字符,后跟一个或多个由管道组成的组,后面跟着记住的字符串)第一组)包括第一次出现Bhaloo的前4个字符,导致正则表达式引擎在处理时使用字符串中的那些字符。对于发现的其余模式也是如此。关键是要包括结束模式,如果正则表达式引擎位于字符串的末尾,它将是结束管道或行尾字符。在这里,我添加了(\||$)的结束模式组,其中显示为'其后是管道或行的末尾'。这可以确保如果字符串碰巧匹配下一个字符串的开头,则它将不会被正则表达式引擎使用。然后替换模式将结束字符串添加为\3以确保它在输出中打印(基本上将其添加回来,因为它通过检查来消耗它)。

SQL> select regexp_replace('ABhal|Bhal|Bhal|Bhal|Bhaloo|Bhaloo|Bhaloo|Bhaloooo|Bhaloooo|Bhaloooo|||||Baker|Baker|Baker',
  2                        '([^|]*)(\|\1)*(\||$)', '\1\3') as unique_values
  3  from dual;

UNIQUE_VALUES
---------------------------------
ABhal|Bhal|Bhaloo|Bhaloooo||Baker

SQL>

编辑:轻微调整在其他值之间处理NULLS。不确定这是多么有用。改变了测试用例。还将正则表达式更改为匹配零或更多而不是一个或多个(星号而不是加号)。

注意事项:

我接受了自己的建议,并测试了意想不到的值。总是期待意外!也许这些可能是你的因素?

这要求列表已经按顺序排列。即如果还有另一个' Bhal'最后,它将被视为一个新值。

Null也不会优雅地处理。好吧,有点。更改了上面的测试用例来说明。

答案 1 :(得分:0)

我不得不添加一个|在字符串的末尾,使它工作,所以它不是最优雅的解决方案,但我相信它的工作原理:

select rtrim(regexp_replace('Bhal|Bhal|Bhal|Bhaloo|Bhaloo|Bhaloo|Bhaloooo|Bhaloooo|Baker|Baker|Baker'||'|'
                    , '([^|]+\|)(\1)+', '\1'),'|')from dual

答案 2 :(得分:0)

我认为问题在于它只是在寻找:

(非管道字符串)(管道符)(在\ 1处找到的字符串)

abc|abcd的情况下将是部分匹配。

这几乎有效:

select regexp_replace(
         'Bhal|Bhal|Bhal|Bhaloo|Bhaloo|Bhaloo|Bhaloooo|Bhaloooo|Bhaloooo|Baker|Baker|Baker'
       , '([^|]+)(\|)(\1\|)+'
       , '\1|' )
from   dual;

虽然它没有抓住最后的Baker,因为它没有跟随管道。如果你不介意再将一个管道字符连接到源字符串的末尾并清理输出,那么就在那里。

答案 3 :(得分:0)

问题已经得到很好的识别,并通过其他答案进行分析。所以我只是在这里添加另一种可能的解决方案 至少,对于问题中给出的测试用例,这会产生预期的输出。

select regexp_replace('Bhal|Bhal|Bhal|Bhaloo|Bhaloo|Bhaloo|Bhaloooo|Bhaloooo|Bhaloooo|Baker|Baker|Baker', '(.+?)(\|)((\1(\2|$))+)', '\1\5') from dual

简要说明: 请注意,Capture组由组开头的左括号编号。

   (  )  (  )  (  (  (  )  )  ) 
   1     2     3  4  5   

此处,组5包含在组4中,组4又包含在组3中。

捕获组1 - > (。+?)匹配一个或多个字符。     这是非贪婪的,所以当正则表达式的下一部分匹配时停止。

The expression given in the question [^|]+ works as well.
This effectively matches one of the words in the string.

捕获组2 - > (\ |)匹配分隔符,这是一个文字' |'

捕获组3 - > ((\ 1(\ 2 | $))+)      这包含组4,组4又包含组5.这匹配了一个"序列中的一个单词后面跟着一个分隔符或字符串的结尾"

捕获组4 - > (\ 1(\ 2 | $))      实际的单词在第1组中匹配,后跟分隔符    (这是第2组)或字符串的结尾

捕获组5 - > (\ 2 | $)      匹配分隔符' |'或者字符串的结尾

答案 4 :(得分:0)

我结合了一些想法,现在使用一个函数返回一个字符串中唯一值的不同排序列表。此方法不要求列表已按其他答案进行排序。

这个SQL也可以在子选择而不是函数中使用。

    function UniqueList (cList varchar2, cNewItem varchar2 default '', cDelim varchar2 default ',')
return varchar2 
is
  cResult varchar2(4000);
begin 
  select distinct listagg(txt,cDelim) WITHIN GROUP (ORDER BY txt) OVER () into cResult
    from (
    select distinct * from (                    
        SELECT REGEXP_SUBSTR (cList||cDelim||cNewItem,'[^'||cDelim||']+',1,LEVEL) TXT
                 FROM DUAL
           CONNECT BY REGEXP_SUBSTR (cList||cDelim||cNewItem,'[^'||cDelim||']+',1,LEVEL)
                 IS NOT NULL
        )
   ); 
    return cResult;
end;