2013-06-22 18 views
5

Onestamente, penso che prima dovrei chiedere il tuo aiuto con la sintassi di questa domanda.Come creare un loop con espressioni regolari?

Ma per favore se riesci a capire cosa intendo, modifica il titolo con uno adatto.

C'è un modo per creare pattern che può dividere un testo come questo.

{{START}} 
    {{START}} 
     {{START}} 
      {{START}} 
      {{END}} 
     {{END}} 
    {{END}} 
{{END}} 

Quindi ogni {{START}} corrisponde al suo {{END}} dall'interno al primo all'ultimo!

E se non riesco a farlo solo con regex. Che ne dici di farlo usando PHP?

Grazie in anticipo.

+5

Non può essere fatto con la maggior parte dei regex, anche se ci sono trucchi, oltre al mio ken, che lo rendono possibile in linguaggi come Perl. Leggi il lemma del pompaggio per scoprire perché non puoi farlo. – siride

+0

suppongo che la tua formattazione sia una specie di input. Se hai spiegato un po 'di più forse potrebbe essere suggerito un approccio alternativo. –

+0

Sembra che tu stia cercando di analizzare qualcosa ... [Se qualcosa è così complesso come l'HTML (mi sembra così), farlo con espressioni regex è una cattiva idea.] (Http://stackoverflow.com/questions/1732348/regex-match-open-tags-except-xhtml-self-contained-tags/1732454 # 1732454) – michaelb958

risposta

4

Questo è oltre la capacità di un'espressione regolare, che può solo analizzare grammatiche regolari. Quello che stai descrivendo richiederebbe un automa pushdown (le lingue regolari sono definite da regular automaton).

È possibile utilizzare l'espressione regolare per analizzare i singoli elementi, ma la parte "profondità" deve essere gestita da una lingua con un concetto di memoria (PHP va bene per questo).

Pertanto, nella propria soluzione, le espressioni regex verranno utilizzate solo per identificare i tag, mentre la logica reale per il rilevamento della profondità e per determinare a quale elemento appartiene il tag END deve essere il proprio programma.

+1

PHP utilizza un motore regex che può più di semplici espressioni regolari. http://pcre.org/pcre.txt - quindi la tua risposta è solo accademica - non di pratica. Comunque puoi usare anche quel motore per fare come lo descrivi. Solo la prima parte non si applica a PHP/PCRE. – hakre

1

Non è possibile farlo con puro RegEx, tuttavia con un semplice ciclo può essere eseguito.

JS Esempio:

//[.\s\S]* ensures line breaks are matched (dotall not supported in JS) 
var exp = /\{\{START\}\}([.\s\S]*)\{\{END\}\}/; 

var myString = "{{START}}\ntest\n{{START}}\ntest 2\n{{START}}\ntest 3\n{{START}}\ntest4\n{{END}}\n{{END}}\n{{END}}\n{{END}}"; 

var matches = []; 
var m = exp.exec(myString); 
while (m != null) { 
    matches.push(m[0]); 
    m = exp.exec(m[1]); 
} 

alert(matches.join("\n\n")); 

PHP (non ho idea se questo è corretto, è stato per sempre da quando ho fatto PHP)

$pattern = "/\{\{START\}\}([.\s\S]*)\{\{END\}\}/"; 
$myString = "{{START}}\ntest\n{{START}}\ntest 2\n{{START}}\ntest 3\n{{START}}\ntest4\n{{END}}\n{{END}}\n{{END}}\n{{END}}"; 

$result = preg_match($pattern, $myString, $matches, PREG_OFFSET_CAPTURE); 
$outMatches = array(); 
while ($result) { 
    array_push($outMatches, $matches[0]); 
    $result = preg_match($pattern, $matches[1], $matches, PREG_OFFSET_CAPTURE); 
} 
print($outMatches); 

uscita:

{{START}} 
test 
{{START}} 
test 2 
{{START}} 
test 3 
{{START}} 
test4 
{{END}} 
{{END}} 
{{END}} 
{{END}} 

{{START}} 
test 2 
{{START}} 
test 3 
{{START}} 
test4 
{{END}} 
{{END}} 
{{END}} 

{{START}} 
test 3 
{{START}} 
test4 
{{END}} 
{{END}} 

{{START}} 
test4 
{{END}} 
+0

L'OP voleva una soluzione PHP. Riprova. – michaelb958

+0

Aggiunto PHP, non ho idea se è corretto o meno. Non ho fatto PHP da anni. –

+0

In PHP regex ha ricorsione, vedi http://pcre.org/ – hakre

2

È possibile! Si può avere ogni livello di contenuti utilizzando un ricorsiva espressione regolare:

$data = <<<LOD 
{{START1}} 
    aaaaa 
    {{START2}} 
     bbbbb 
     {{START3}} 
      ccccc 
      {{START4}} 
       ddddd 
      {{END4}} 
     {{END3}} 
    {{END2}} 
{{END1}} 
LOD; 

$pattern = '~(?=({{START\d+}}(?>[^{]++|(?1))*{{END\d+}}))~'; 
preg_match_all ($pattern, $data, $matches); 

print_r($matches); 

spiegazioni:

parte: ({{START\d+}}(?>[^{]++|(?1))*{{END\d+}})

Questa parte del modello descrive una struttura annidata con {{START#}} e {{END#}}

(   # open the first capturing group 
{{START\d+}} 
(?>   # open an atomic group (= backtracks forbidden) 
    [^{]++ # all that is not a { one or more times (possessive) 
    |   # OR 
    (?1)  # refer to the first capturing group itself 
)    # close the atomic group 
{END\d+}}  # 
)    # close the first capturing group 

Ora il problema è che non è possibile acquisire tutto il livello con questa parte solo, perché tutti i caratteri della stringa sono consumati dal modello. In altre parole, non è possibile abbinare parti sovrapposte della stringa.

Il problema è quello di avvolgere tutta questa parte all'interno di un'asserzione a lunghezza zero, che non consuma personaggi come un lookahead (?=...), risultato:

(?=({{START\d+}}(?>[^{]++|(?1))*{{END\d+}})) 

Ciò corrisponderà a tutti i livelli.