Isso deve funcionar desde que você não tenha uma entrada parecida com
%ABC#%ABC#
SELECT REGEXP_REPLACE( '%ABC#abc\%ABC#', '((^|[^\])(\\\\)*)%ABC#', '\1XXX' )
FROM DUAL;
Isso irá corresponder:
- O início da string
^
ou um caractere sem barra[^\]
seguido por qualquer número de pares de caracteres de barra e, finalmente, os caracteres%ABC#
. Isso corresponderá a%ABC#
,\\%ABC#
,\\\\%ABC#
e assim por diante, mas não corresponderá a\%ABC#
,\\\%ABC#
,\\\\\%ABC#
onde há uma barra escapando do%
personagem.
A substituição inclui o primeiro grupo de captura, pois a expressão pode corresponder a um caractere anterior sem barra e a pares de barras e eles precisam ser preservados na saída.
Atualizar
Isso fica um pouco complicado, mas fará correspondências repetidas:
WITH Data ( VALUE ) AS (
SELECT '%ABC#%ABC#' FROM DUAL
)
SELECT ( SELECT LISTAGG(
REGEXP_REPLACE( COLUMN_VALUE, '((^|[^\])(\\\\)*)%ABC#$', '\1XXX' ),
NULL
) WITHIN GROUP ( ORDER BY NULL )
FROM TABLE(
CAST(
MULTISET(
SELECT REGEXP_SUBSTR( d.value, '.*?(%ABC#|$)', 1, LEVEL )
FROM DUAL
CONNECT BY LEVEL < REGEXP_COUNT( d.value, '.*?(%ABC#|$)' )
AS SYS.ODCIVARCHAR2LIST
)
)
) AS Value
FROM Data d;
Ele usa uma subconsulta correlacionada para dividir a string em substrings que terminam com
%ABC#
ou o final da string (este é o bit dentro da TABLE( CAST( MULTISET( ) .. ) )
) e, em seguida, re-concatena essas substrings após realizar a substituição no final de cada substring.