To powinno działać, pod warunkiem, że nie masz danych wejściowych, które wyglądają jak %ABC#%ABC#
SELECT REGEXP_REPLACE( '%ABC#abc\%ABC#', '((^|[^\])(\\\\)*)%ABC#', '\1XXX' )
FROM DUAL;
To będzie pasować:
- Początek ciągu
^lub znak inny niż ukośnik[^\]po którym następuje dowolna liczba par znaków ukośnika, a na końcu znaki%ABC#. To będzie pasować do%ABC#,\\%ABC#,\\\\%ABC#i tak dalej, ale nie pasuje do\%ABC#,\\\%ABC#,\\\\\%ABC#gdzie jest ukośnik poprzedzający%postać.
Zastąpienie obejmuje pierwszą grupę przechwytywania, ponieważ wyrażenie może pasować do poprzedzającego znaku bez ukośnika i par ukośników, które muszą być zachowane w danych wyjściowych.
Aktualizacja
To trochę się komplikuje, ale spowoduje powtarzające się dopasowania:
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;
Używa skorelowanego podzapytania, aby podzielić ciąg na podciągi, które kończą się %ABC# lub koniec ciągu (jest to bit wewnątrz TABLE( CAST( MULTISET( ) .. ) ) ), a następnie ponownie łączy te podciągi po wykonaniu zamiany na końcu każdego podciągu.