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.