通过 Oracle 的 REGEXP_SUBSTR 排除正则表达式中的一系列字符

Posted

技术标签:

【中文标题】通过 Oracle 的 REGEXP_SUBSTR 排除正则表达式中的一系列字符【英文标题】:Exclude a series of characters in regex via Oracle's REGEXP_SUBSTR 【发布时间】:2018-05-22 12:44:33 【问题描述】:

我正在尝试使用 Oracle 的 REGEXP_SUBSTR 来选择字符串中的字段。

例子:

this,,,is,,,an,,,example

解决方案:

DECLARE
  field1 VARCHAR2(4000);
  field2 VARCHAR2(4000);
  field3 VARCHAR2(4000);
  field4 VARCHAR2(4000);
  separator VARCHAR2(300) := ',,,';
  lineToParse VARCHAR2(4000) := 'this,,,is,,,an,,,example';
BEGIN
  SELECT REGEXP_SUBSTR(lineToParse, '[^' || separator || ']+', 1, 1) AS part_1, REGEXP_SUBSTR(lineToParse, '[^' || separator || ']+', 1, 2) AS part_2, REGEXP_SUBSTR(lineToParse, '[^' || separator || ']+', 1, 3) AS part_3, REGEXP_SUBSTR(lineToParse, '[^' || separator || ']+', 1, 4) AS part_4
  INTO field1, field2, field3, field4
  FROM DUAL;
  DBMS_OUTPUT.PUT_LINE('Field 1: ' || field1);
  DBMS_OUTPUT.PUT_LINE('Field 2: ' || field2);
  DBMS_OUTPUT.PUT_LINE('Field 3: ' || field3);
  DBMS_OUTPUT.PUT_LINE('Field 4: ' || field4); 
END;

这非常适合上面的行,生成:

Field 1: this
Field 2: is
Field 3: an
Field 4: example

但是对于下面的行,它没有

this,,,is, a perfectly fine,,,new,,, line

这是因为第二个捕获组应该是:“是,完全没问题” 但最终成为“是”。

输出是:

Field 1: this
Field 2: is
Field 3:  a perfectly fine
Field 4: new

原因是我使用的正则表达式:

[^,,,]+

正在捕获 ^ 后面的任何字符而不是序列。

如何调整我的正则表达式以便捕获整个序列?

这需要与 Oracle 11g 兼容。

【问题讨论】:

【参考方案1】:

我不认为你可以在这里做一个简单的正则表达式。首先,您使用的字符类 [^,,,][^,] 没有任何不同 - 方括号中的重复字符不会导致要匹配的字符串中的重复字符。其次,我认为否定匹配不会起作用,因为 Oracle 正则表达式不支持环视。

您可以尝试以下方法:

SELECT REGEXP_SUBSTR(lineToParse, '.+?($|' || separator || ')', 1, 1) AS part_1
     , REGEXP_SUBSTR(lineToParse, '.+?($|' || separator || ')', 1, 2) AS part_2
     , REGEXP_SUBSTR(lineToParse, '.+?($|' || separator || ')', 1, 3) AS part_3
     , REGEXP_SUBSTR(lineToParse, '.+?($|' || separator || ')', 1, 4) AS part_4
  INTO field1, field2, field3, field4
  FROM DUAL;

这将使所有内容以非贪婪的方式到达分隔符或行尾。现在唯一的问题是返回的值可能包含分隔符;有一些方法可以避免这种情况,其中最直接的方法是使用 REPLACE(),但在 Oracle 11 中,您还可以使用带有 REGEXP_SUBSTR() 的子表达式:

SELECT REGEXP_SUBSTR(lineToParse, '(.+?)($|' || separator || ')', 1, 1, 'c', 1) AS part_1
     , REGEXP_SUBSTR(lineToParse, '(.+?)($|' || separator || ')', 1, 2, 'c', 1) AS part_2
     , REGEXP_SUBSTR(lineToParse, '(.+?)($|' || separator || ')', 1, 3, 'c', 1) AS part_3
     , REGEXP_SUBSTR(lineToParse, '(.+?)($|' || separator || ')', 1, 4, 'c', 1) AS part_4
  INTO field1, field2, field3, field4
  FROM DUAL;

但是,如果lineToParse 以分隔符开头,那么您仍然需要以某种方式处理它。将REGEXP_SUBSTR() 的第一个实例更改为此似乎可行:

REGEXP_SUBSTR(lineToParse, '^(' || separator || ')?(.+?)($|' || separator || ')', 1, 1, 'c', 2) AS part_1

希望这会有所帮助。

【讨论】:

谢谢!我自己找到了相同的解决方案。我还为四个字段中的每一个使用了 field1 := REPLACE(field1, separator, '') 来删除结尾的逗号。【参考方案2】:

只需将您的正则表达式调用更改为:

REGEXP_SUBSTR(lineToParse, '(.*?)(' || separator || '|$)', 1, 1, NULL, 1) AS part_1,

这定义了一组字符,后跟一组由分隔符或行尾组成的组。它匹配该组的第 1 次出现(第 4 个参数)并返回第 1 个组(第 6 个参数)。

奖金! This regex form handles NULL list elements too, where the form '[^,]' does not!

更好的是,构建一个可以放入实用程序包中的函数,以便重用,封装此功能并调用它。这样,对正则表达式不满意的人可以使用它,如果需要更改,您只需在一个地方更改正则表达式代码:

FUNCTION  GET_LIST_ELEMENT(string_in VARCHAR2, element_in NUMBER, delimiter_in VARCHAR2 DEFAULT ',') RETURN VARCHAR2 IS
    BEGIN
      if string_in is null then
        return NULL;
      else
        RETURN REGEXP_SUBSTR(string_in, '(.*?)(\' || delimiter_in || '|$)', 1, element_in, NULL, 1);
      end if;
  END GET_LIST_ELEMENT;

那么您的代码将如下所示:

util.get_list_element(lineToParse, 1, separator) AS part_1, 
util.get_list_element(lineToParse, 2, separator) AS part_2, 
util.get_list_element(lineToParse, 3, separator) AS part_3, 
util.get_list_element(lineToParse, 4, separator) AS part_4

【讨论】:

以上是关于通过 Oracle 的 REGEXP_SUBSTR 排除正则表达式中的一系列字符的主要内容,如果未能解决你的问题,请参考以下文章

Oracle中REGEXP_SUBSTR函数

在 Oracle 中使用 regexp_substr 按顺序拆分字符串

Oracle 字符串转多行(REGEXP_SUBSTR)

Oracle中REGEXP_SUBSTR及其它支持正则表达式的内置函数小结

Oracle SQL 正则表达式 (regexp_substr)

在 Oracle 中使用 REGEXP_SUBSTR 作为拆分