Последовательная замена шаблона не происходит с REGEXP_REPLACE

У меня есть строка, как показано ниже

Welcome to the world of the Hackers

Я пытаюсь заменить вхождения перечисленных строк, то есть of,to,the между всей строкой, используя приведенный ниже запрос, но он не работает должным образом, если шаблоны являются последовательными, это не удается.

SELECT regexp_replace( 'Welcome to the world of the Hackers', '( to )|( the )|( of )', ' ' ) 
FROM dual;

Выход: Welcome the world the Hackers

Даже если шаблон повторяется последовательно, он не работает, т.е.

SELECT regexp_replace( 'Welcome to to the world of the Hackers', '( to )|( the )|( of )', ' ' ) 
FROM dual;

Выход: Welcome to world the Hackers

В то время как мой ожидаемый результат: Welcome world Hackers

Есть ли альтернатива / решение для этого с использованием REGEXP_REPLACE?


person Aspirant    schedule 05.06.2018    source источник


Ответы (2)


Вы можете использовать регулярное выражение (^|\s+)((to|the|of)(\s+|$))+:

SQL Fiddle

Запрос 1:

WITH test_data ( sentence ) AS (
  SELECT 'to the of' FROM DUAL UNION ALL
  SELECT 'woof breathe toto' FROM DUAL UNION ALL -- has all the words as sub-strings of words
  SELECT 'theory of the offer to total' FROM DUAL -- mix of words to replace and words starting with those words
)
SELECT sentence,
       regexp_replace(
         sentence,
         '(^|\s+)((to|the|of)(\s+|$))+',
         '\1'
       ) AS replaced
FROM   test_data

Результаты:

|                     SENTENCE |           REPLACED |
|------------------------------|--------------------|
|                    to the of |             (null) | -- All words replaced
|            woof breathe toto |  woof breathe toto |
| theory of the offer to total | theory offer total |

Почему regexp_replace( 'Welcome to the world of the Hackers', '( to )|( the )|( of )', ' ' ) не работает с последовательными совпадениями?

Поскольку синтаксический анализатор регулярных выражений будет искать второе совпадение после конца первого совпадения и не будет включать уже проанализированную часть строки или текст замены при поиске последующих совпадений.

Итак, первый матч будет:

 'Welcome to the world of the Hackers'
         ^^^^

Второе совпадение будет выглядеть в подстроке, следующей за этим совпадением.

 'the world of the Hackers'
           ^^^^

'the ' в начале подстроки не будет сопоставлен, поскольку у него нет ведущего символа пробела (да, перед ним был пробел, но он был сопоставлен в предыдущем сопоставлении, и, да, это совпадение было заменено пробелом, но перекрывающиеся совпадения и совпадения с предыдущими заменами - это не то, как работают регулярные выражения).

Таким образом, второе совпадение - это ' of ' в середине оставшейся подстроки.

Третьего совпадения не будет, так как оставшаяся не проанализированная подстрока:

'the Hackers'

и, опять же, 'the ' не соответствует, так как нет ведущего символа пробела для сопоставления.

person MT0    schedule 05.06.2018

REGEXP_REPLACE не соответствует второму шаблону, который является частью уже согласованного шаблона. Это становится более очевидным, когда вы используете сопоставление по нескольким образцам, например |. Таким образом, вы не можете полагаться на пробелы для границ слов, чтобы таким образом соответствовать нескольким шаблонам. Одним из решений может быть разделение и объединение персонажей. Возможно, это не лучший способ, но, тем не менее, он работает. Буду рад узнать лучшее решение.

Это также предполагает, что вы согласны с одиночными пробелами в объединенной строке, когда их было более одного в исходной строке. Кроме того, не рассматриваются слова, заканчивающиеся запятой или точкой с запятой. В таких случаях вы можете улучшить его, используя NOT REGEXP_LIKE вместо NOT IN.

WITH t (id,s)
AS (
    SELECT 1 , 'Welcome to the world of the Hackers, you told me these words at the'
      FROM DUAL
      UNION ALL
    SELECT 2, 'The second line.Welcome to the world of the Hackers, you told me these words at the'
    FROM DUAL
    )
SELECT LISTAGG(word, ' ') WITHIN
GROUP (
        ORDER BY w
        )
FROM (
    SELECT id,
          LEVEL AS w
        ,REGEXP_SUBSTR(s, '[^ ]+', 1, LEVEL) AS word
    FROM t CONNECT BY LEVEL <= REGEXP_COUNT(s, '[^ ]+')
   AND PRIOR id = id 
  AND PRIOR SYS_GUID() IS NOT NULL

    )
WHERE lower(word) NOT IN (
        'to'
        ,'the'
        ,'of'
        )
        GROUP BY id;

Демо

person Kaushik Nayak    schedule 05.06.2018