为什么这个正则表达式执行部分匹配?

时间:2018-05-18 15:52:57

标签: regex oracle

我有以下原始数据:

1.1.2.2.4.4.4.5.5.9.11.15.16.16.19 ...

我正在使用此正则表达式删除重复项:

([^.]+)(.[ ]*\1)+

导致以下结果:

1.2.4.5.9.115.16.19 ...

问题是正则表达式如何处理子串1.1 .11.15.应该是什么9.11.15.16变为9.115.16。我该如何解决这个问题?

原始值按数字顺序排序,以适应用于处理重复值的正则表达式。

正则表达式正在Oracle REGEXP_REPLACE

中使用

小数是分隔符。我尝试过逗号和管道,但这并没有解决问题。

4 个答案:

答案 0 :(得分:3)

Oracle的REGEX无法按照您的预期运作。您可以使用常规方法Splitting string into multiple rows in Oracle拆分字符串并查找不同的行。另一种选择是使用XMLTABLE,它适用于数字以及适当引用的字符串。

SELECT LISTAGG(n, '.') WITHIN
GROUP (
        ORDER BY n
        ) AS n
FROM (
    SELECT DISTINCT TO_NUMBER(column_value) AS n
    FROM XMLTABLE(replace('1.1.2.2.4.4.4.5.5.9.11.15.16.16.19', '.', ','))
    );

Demo

答案 1 :(得分:1)

不幸的是,Oracle没有提供与字边界位置匹配的令牌。无论是熟悉的(freq_df.sort_values('count', ascending=False) .groupby(['open_year','open_month'], sort=False).head(5) ) 令牌还是古老的\b[[:<:]]

但是在这个特定的集合上你可以使用:

[[:>:]]

注意:你忘了逃避点。

答案 2 :(得分:1)

你的正则表达式抓住了:

  • 1 - 11中的第二个数字,
  • 然后是一个点,
  • ,最后是1 - 15中的第一个数字。

所以你的正则表达式无法捕捉整个数字序列。

编写正则表达式的最自然的方法是捕捉整个序列 数字将使用:

  • 字符串开头或点的隐藏,
  • 然后捕捉一系列数字,
  • 最后是一个点的前瞻。

但是我写道,由于我不确定Oracle是否支持外观 正则表达式的另一种方式:

(^|\.)(\d+)(\.(\2))+

详细说明:

  • (^|\.) - 字符串的开头或点(组1),而不是 loobehind。
  • (\d+) - 一系列数字(第2组)。
  • ( - 从第3组开始,包含:
  • \.(\2) - 一个点和相同的数字序列,它们捕获了第2组。
  • )+ - 第3组结束,可能会多次出现。

答案 3 :(得分:0)

将重复模式分组并将其删除

正如revo指出的那样,你的困难的一个重要来源是没有逃避这段时期。此外,包含115的结果字符串可以解释如下(Valdi_Bo之前做了类似的观察):

([^.]+)(.[ ]*\1)+将匹配11.15,如下所示:

SCOTT@DB>SELECT
  2      '11.15' val,
  3      regexp_replace('11.15','([^.]+)(\.[ ]*\1)+','\1') deduplicated
  4  FROM
  5      dual;
VAL     DEDUPLICATED
11.15   115

以下是解决这些问题的类似方法:

匹配模式组合

- 查找长度为0到N的非句点匹配列表(子表达式由\1引用)。

匹配([^.]*)

'19'

- 查找形成与子表达式2关联的第二个匹配列表的重复项,由\2引用。

'19 .19.19'匹配([^.]*)([.]\1)+

- 查找字符串的句点或结尾。这是\3引用的匹配列表。这将'11 .15'的匹配修复为'115'。

([.]|$)

替换字符串

我将匹配模式替换为由非句点匹配列表的第一个实例组成的替换字符串。

\1\3

<强>解决方案

regexp_replace(val,'([^.]*)([.]\1)+([.]|$)','\1\3')

以下是使用示例的一些排列的示例:

SCOTT@db>WITH tst AS (
  2      SELECT
  3          '1.1.2.2.4.4.4.5.5.9.11.15.16.16.19' val
  4      FROM
  5          dual
  6      UNION ALL
  7      SELECT
  8          '1.1.1.1.2.2.4.4.4.4.4.5.5.9.11.11.11.15.16.16.19' val
  9      FROM
 10          dual
 11      UNION ALL
 12      SELECT
 13          '1.1.2.2.4.4.4.5.5.9.11.15.16.16.19.19.19' val
 14      FROM
 15          dual
 16  ) SELECT
 17      val,
 18      regexp_replace(val,'([^.]*)([.]\1)+([.]|$)','\1\3') deduplicate 
 19      FROM
 20      tst;
VAL                                                DEDUPLICATE             
------------------------------------------------------------------------
1.1.2.2.4.4.4.5.5.9.11.15.16.16.19                 1.2.4.5.9.11.15.16.19   
1.1.1.1.2.2.4.4.4.4.4.5.5.9.11.11.11.15.16.16.19   1.2.4.5.9.11.15.16.19   
1.1.2.2.4.4.4.5.5.9.11.15.16.16.19.19.19           1.2.4.5.9.11.15.16.19   

我的方法没有解决字符串中可能的空格。可以单独删除它们(例如通过单独的替换语句)。