Esto debería funcionar siempre que no tenga una entrada que se parezca a %ABC#%ABC#
SELECT REGEXP_REPLACE( '%ABC#abc\%ABC#', '((^|[^\])(\\\\)*)%ABC#', '\1XXX' )
FROM DUAL;
Esto coincidirá con:
- El comienzo de la cadena
^
o un carácter sin barra[^\]
seguido de cualquier número de pares de caracteres de barra y, finalmente, los caracteres%ABC#
. Esto coincidirá con%ABC#
,\\%ABC#
,\\\\%ABC#
y así sucesivamente, pero no coincidirá con\%ABC#
,\\\%ABC#
,\\\\\%ABC#
donde hay una barra que escapa del%
personaje.
El reemplazo incluye el primer grupo de captura, ya que la expresión puede coincidir con un carácter anterior que no sea una barra oblicua y pares de barras, y estos deben conservarse en la salida.
Actualizar
Esto se vuelve un poco complicado pero hará coincidencias 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;
Utiliza una subconsulta correlacionada para dividir la cadena en subcadenas que terminan con %ABC#
o el final de la cadena (este es el bit dentro de TABLE( CAST( MULTISET( ) .. ) )
) y luego vuelve a concatenar estas subcadenas después de realizar el reemplazo al final de cada subcadena.