2015-02-13 11 views
11

Ho i seguenti dati di seguito dove {n} rappresenta un segnaposto.Sostituire ogni istanza tra due caratteri

{n}{n}A{n}{n}A{n} 
{n}A{n}{n}{n}{n}A 
{n}{n}A{n}A{n}{n} 
{n}{n}{n}A{n}A{n}B 
{n}A{n}{n}B{n}{n} 
A{n}B{n}{n}{n}{n} 

desidero sostituire ogni istanza del segnaposto tra due lettere A con per esempio la lettera C. Ho scritto il seguente regex per questo e sto usando la funzione preg_replace.

$str = preg_replace('~(?<=A)(\{n\})*(?=A)~', 'C', $str); 

Il problema è che sostituisce tutte le istanze tra due A di con un C. Come posso correggere la mia espressione regolare o la chiamata preg_replace per sostituire ogni singola istanza dei segnaposto con C?

Questa dovrebbe essere la mia uscita.

{n}{n}ACCA{n} 
{n}ACCCCA 
{n}{n}ACA{n}{n} 
{n}{n}{n}ACA{n}B 
{n}A{n}{n}B{n}{n} 
A{n}B{n}{n}{n}{n} 

Ma attualmente emette questo.

{n}{n}ACA{n} 
{n}ACA 
{n}{n}ACA{n}{n} 
{n}{n}{n}ACA{n}B 
{n}A{n}{n}B{n}{n} 
A{n}B{n}{n}{n}{n} 
+0

Cosa succede in questa situazione: 'A {n} A {n} A'? 'ACA {n} A' o' ACACA'? –

risposta

8

È possibile risolvere il problema ancorando con \G.

$str = preg_replace('~(?:\G(?!\A)|({n})*A(?=(?1)++A))\K{n}~', 'C', $str); 

Il \G caratteristica è un ancoraggio che può corrispondere ad una delle due posizioni; l'inizio della posizione della corda o la posizione alla fine dell'ultima partita. La sequenza di escape \K reimposta il punto iniziale della corrispondenza riportata e tutti i caratteri consumati in precedenza non sono più inclusi.

Per ridurre la quantità di backtracking, si potrebbe usare un'espressione più complessa:

$str = preg_replace('~\G(?!\A)(?:{n} 
         |A(?:[^A]*A)+?((?=(?:{n})++A)\K{n} 
         |(*COMMIT)(*F))) 
         |[^A]*A(?:[^A]*A)*?(?1)~x', 'C', $str); 
+3

[+] (http://en.wikipedia.org/wiki/Plus_and_minus_signs) 1 per l'uso fantastico di '\ K'. – Unihedron

4
(?<=A){n}(?=(?:{n})*A)|\G(?!^){n} 

si può provare questo. Sostituire con C. Qui devi usare \G per affermare la posizione alla fine della partita precedente o l'inizio della stringa per la prima partita.

In modo che è possibile abbinare dopo la prima partita. Guarda la demo.

https://regex101.com/r/wU4xK1/7

Qui prima si abbinare {n} che ha A dietro e A dopo che che può avere {n} in mezzo. Dopo l'acquisizione, si utilizza \G per ripristinare alla fine della corrispondenza precedente e successivamente continuare a sostituire {n} trovato.

$re = "/(?<=A){n}(?=(?:{n})*A)|\\G(?!^){n}/"; 
$str = "{n}{n}A{n}{n}A{n}\n{n}A{n}{n}{n}{n}A\n{n}{n}A{n}A{n}{n}\n{n}{n}{n}A{n}A{n}B\n{n}A{n}{n}B{n}{n}\nA{n}B{n}{n}{n}{n}"; 
$subst = "C"; 

$result = preg_replace($re, $subst, $str); 
7

La soluzione un po 'più prolissa ma più facile da seguire è utilizzare l'espressione iniziale per suddividere il testo in gruppi; quindi applicare la trasformazione individuale all'interno di ogni gruppo:

$text = preg_replace_callback('~(?<=A)(?:\{n\})*(?=A)~', function($match) { 
    // simple replacement inside 
    return str_replace('{n}', 'C', $match[0]); 
}, $text); 

Ho fatto un piccolo tweak per l'espressione di sbarazzarsi della cattura di memoria, che è inutile, utilizzando (?:...).

+2

Onestamente, probabilmente una soluzione più veloce se le prestazioni sono un problema. (+1) – hwnd