Sotto-regole a riconoscimento singolo (Once-only subpatterns)

Con l'indicazione del numero minimo e massimo di ripetizioni, il fallimento del riconoscimento della parte successiva del testo causa una ripetizione dell'identificazione con un numero di occorrenze diverse per verificare se in questa situazione anche il resto del testo viene identificato. In alcune situazioni può essere utile bloccare questo meccanismo, per la cambiata natura del criterio, oppure per fare fallire la ricerca prima di quando potrebbe accadere, oppure quando l'autore sa che non ci sono punti da cui ripartire.

Consideriamo, ad esempio, il criterio \d+foo applicato alla seguente linea 123456bar

Dopo avere identificato le 6 cifre ed avere mancato nel riconoscimento di "foo", il comportamento normale consiste nel tentare di procedere identificando 5 cifre per l'elemento \d+, quindi tentare con 4 e così via, prima di fallire definitivamente. Le sotto-regole a riconoscimento singolo permettono di specificare che, una volta riconosciuta una porzione della regola, questa non debba essere più considerata, in maniera tale da abortire immediatamente il riconoscimento se non si ha successo nell'identificare la parte restante del criterio. L'espressione per definire questo tipo di sotto-regola richiede un altro uso delle parentesi, iniziando con (?> come nell'esempio seguente: (?>\d+)bar

Questa tipologia di parentesi "inchioda" la parte di criterio una volta che avviene il riconoscimento, e, quindi, un fallimento successivo impedisce la ri-elaborazione di questo segmento. Tuttavia, occorre notare che gli elementi precedenti a questo si comportano in modo normale, e pertanto la ri-elaborazione, pur non toccando questo elemento, passa ai precedenti.

Una descrizione alternativa di questa tipologia di sotto-regola potrebbe essere che questo criterio identifica un testo come avrebbe fatto un singolo criterio se fosse stato ancorato alla posizione corrente.

Le sotto-regole a riconoscimento singolo non compiono la cattura del testo identificato. I casi semplici illustrati in precedenza possono essere considerati come una estremizzazione della ripetizione che porta ad inglobare tutto ciò che può. Pertanto, da una parte \d+ e \d+? sono sequenze che si adattano a riconoscere il numero corretto di cifre affinchè la ricerca abbia successo, dall'altra la sequenza (?>\d+) riconosce soltanto una sequenza di cifre.

Ovviamente queste costruzioni possono contenere diverse sotto-regole sia complesse, sia annidate.

Le sotto-regole a riconoscimento singolo possono essere usate congiuntamente alle asserzioni che guardano indietro, per definire una regola efficiente per riconoscere la fine della stringa. Ad esempio si consideri questo semplice criterio: abcd$ quando viene applicato ad un lungo testo può non avere successo. Questo perché il riconoscimento procede da sinistra verso destra, quindi PCRE prima cerca la "a", e poi cerca di riconoscere la parte restante del criterio. Se si modifica il criterio nel seguente modo ^.*abcd$ allora la sequenza iniziale .* in prima battuta identificherà tutto il testo, ma quando fallisce (poiché non vi sono più "a"), la ricerca tornerà indietro di uno (quindi tutto il testo tranne l'ultimo carattere), quindi, se continua non esserci la "a", si torna indietro di due, e così via. Continuando a tornare indietro alla ricerca della "a" si percorre tutto il testo da destra a sinistra, senza ottenere nulla di valido. Tuttavia se si riscrive il criterio come: ^(?>.*)(?<=abcd) non si attiva più lo scorrimento verso sinistra per .* , ma viene costretto a riconoscere tutto il testo (esito del primo tentativo). La asserzione successiva, esegue una verifica sulle ultime 4 lettere.Se fallisce, ciò avviene immediatamente, provocando un impatto sensibile nei tempi di elaborazione con testi molto lunghi.

Quando un criterio di riconoscimento contiene un elemento ripetuto senza limite all'interno di una sotto-regola che anch'essa possa ripetuta illimitatamente, l'uso delle sotto-regole a riconoscimento singolo è l'unico mezzo per evitare che certi mancati riconoscimenti richiedano molto tempo per essere rilevati. Ad esempio il criterio (\D+|<\d+>)*[!?] riconosce un numero indefinito di frammenti di testo contenenti a loro volta numeri o caratteri non numerici racchiusi tra <>, seguiti dai caratteri ! o ?. Quando il riconoscimento ha successo l'esecuzione è rapida, ma quando viene applicato al testo aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa richiede molto tempo prima di evidenziare l'errore. Questo accade perché la stringa può essere suddivisa tra i due elementi ripetuti in un elevato numero di modi che debbono essere verificati. Nell'esempio si usa [!?] piuttosto che un singolo carattere alla fine, questo perché sia Perl sia PCRE hanno una ottimizzazione che permette un veloce riconoscimento dell'errore se si usa un solo carattere. Infatti memorizzano l'ultimo carattere singolo richiesto per un riconoscimento, e, se non lo trovano nel testo, falliscono subito. Se il criterio viene modificato in ((?>\D+)|<\d+>)*[!?] le sequenze di caratteri non numerici non possono essere interrotte e pertanto il mancato riconoscimento viene rilevato più velocemente.

add a note

User Contributed Notes 1 note

up
4
Anonymous
2 years ago
Never put a "once-only subpattern" (?>...) in a "one-line" comment (#, //).
I spent almost 1 day to fix it.
Use a 'C' style comment instead !

PHP Manual says, a "one-line" comment (# , //) ends just before "?>" (PHP end tag).

These letters "?>" in a "one-line" comment (# , //) seems evaluated as a PHP end tag.

<?php

/* (?> */
echo '"C" style comment works !<br>';

# (?>
echo '"one-line" comment';

?>
To Top