Cela devrait fonctionner à condition que vous n'ayez pas une entrée qui ressemble à %ABC#%ABC#
SELECT REGEXP_REPLACE( '%ABC#abc\%ABC#', '((^|[^\])(\\\\)*)%ABC#', '\1XXX' )
FROM DUAL;
Cela correspondra soit :
- Le début de la chaîne
^
ou un caractère non-slash[^\]
suivi d'un nombre quelconque de paires de barres obliques puis, enfin, des caractères%ABC#
. Cela correspondra à%ABC#
,\\%ABC#
,\\\\%ABC#
et ainsi de suite mais ne correspondra pas à\%ABC#
,\\\%ABC#
,\\\\\%ABC#
où il y a une barre oblique échappant au%
caractère.
Le remplacement inclut le premier groupe de capture car l'expression peut correspondre à un caractère non-slash précédent et à des paires de barres obliques et ceux-ci doivent être conservés dans la sortie.
Mettre à jour
Cela devient un peu compliqué mais cela fera des correspondances répétées :
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;
Il utilise une sous-requête corrélée pour diviser la chaîne en sous-chaînes qui se terminent par %ABC#
ou la fin de chaîne (c'est le bit à l'intérieur du TABLE( CAST( MULTISET( ) .. ) )
) puis reconcatène ces sous-chaînes après avoir effectué le remplacement à la fin de chaque sous-chaîne.