prueba este:
'~(?P<opening>\{(?P<inverse>[!])?block:(?P<name>[a-z0-9\s_-]+)\})(?P<contents>[^{]*(?:\{(?!/block:(?P=name)\})[^{]*)*)(?P<closing>\{/block:(?P=name)\})~i'
O, en forma legible:
'~(?P<opening>
\{
(?P<inverse>[!])?
block:
(?P<name>[a-z0-9\s_-]+)
\}
)
(?P<contents>
[^{]*(?:\{(?!/block:(?P=name)\})[^{]*)*
)
(?P<closing>
\{
/block:(?P=name)
\}
)~ix'
La parte más importante está en el (?P<contents>..)
grupo:
[^{]*(?:\{(?!/block:(?P=name)\})[^{]*)*
Para empezar, el único personaje que nos interesa es el corsé de apertura, por lo que podemos sorber cualquier otro personaje con [^{]*
. Solo después de ver un {
comprobamos si es el comienzo de una etiqueta {/block}
. Si no es así, seguimos consumiéndolo y comenzamos a buscar el siguiente, y lo repetimos según sea necesario.
Usando RegexBuddy, probé cada expresión regular colocando el cursor al comienzo de la etiqueta {block:sponsors}
y la depuración. Luego eliminé la llave de cierre de la etiqueta de cierre {/block:sponsors}
para forzar una coincidencia fallida y la depuré de nuevo. Su expresión regular tomó 940 pasos para tener éxito y 2265 pasos para fallar. El mío dio 57 pasos para tener éxito y 83 pasos para fallar.
En una nota lateral, eliminé el modificador s
porque porque no estoy usando el punto (.
), y el modificador m
porque nunca fue necesario. También utilicé la referencia inversa nombrada (?P=name)
en lugar de \3
según la excelente sugerencia de @ DaveRandom. Y escapé de todas las llaves ({
y }
) porque me resulta más fácil leer de esa manera.
EDIT: Si desea hacer coincidir la más interna bloque de llamada, cambie la porción media de la expresión regular de esto:
(?P<contents>
[^{]*(?:\{(?!/block:(?P=name)\})[^{]*)*
)
... a esto (según lo sugerido por @ Kobi en su comentario):
(?P<contents>
[^{]*(?:\{(?!/?block:[a-z0-9\s_-]+\})[^{]*)*
)
Originalmente, el grupo (?P<opening>...)
agarraba la primera etiqueta de apertura se saw, entonces el grupo (?P<contents>..)
consumiría cualquier cosa, incluidas otras etiquetas, siempre que no fuera la etiqueta de cierre para coincidir con la encontrada por el grupo (?P<opening>...)
. (A continuación, el grupo (?P<closing>...)
seguirá adelante y consumir eso.)
Ahora, el grupo (?P<contents>...)
se niega a igualar cualquier etiqueta, abrir o cerrar (nótese el /?
al principio), no importa cuál es el nombre. Por lo tanto, la expresión regular inicialmente comienza a coincidir con la etiqueta {block:sponsors}
, pero cuando encuentra la etiqueta {block:test}
, abandona esa coincidencia y vuelve a buscar una etiqueta de apertura. Comienza de nuevo en la etiqueta {block:test}
, esta vez completando con éxito la coincidencia cuando encuentra la etiqueta de cierre {/block:test}
.
Parece ineficaz describirlo así, pero en realidad no es así. El truco que describí antes, sorber los no brackets, ahoga el efecto de estos inicios en falso. Donde hacía una búsqueda negativa en casi todas las posiciones, ahora solo está haciendo una cuando encuentra un {
.Incluso se puede utilizar cuantificadores posesivos, como se sugirió como @godspeedlee:
(?P<contents>
[^{]*+(?:\{(?!/?block:[a-z0-9\s_-]+\})[^{]*+)*+
)
... porque usted sabe que nunca va a consumir todo lo que se tendrá que dar de nuevo más tarde. Eso aceleraría un poco las cosas, pero no es realmente necesario.
extraño como suena, una vez tuve una expresión regular que mantuvo comiendo una instancia de Apache en particular y la bandera 'S' (* mayúscula! *) lo arregló. Supuse que era una pérdida de memoria no reportada o algo así y el proceso de estudio hizo que se evitara. Posesión remota, pero vale la pena intentarlo, diría ... – DaveRandom
@DaveRandom, tuve el mismo problema una vez, con la misma solución! Veamos si eso lo hace para el OP –
Otro pensamiento que ocurre es que escapando de los caracteres '{}' literales en la expresión regular podría ayudar. Técnicamente, son metacaracteres y, aunque PCRE parece ser bastante indulgente con las llaves, no le queda mucho trabajo por hacer si se le escapa correctamente. ¿Por qué usar grupos de captura con nombre y no usar los nombres en las referencias? '/ block: \ 3' =>'/block :(? P = name) '. Esto es especialmente cierto para su expresión regular ya que '' es opcional, en cuyo caso '' será '\ 2', no' \ 3' –
DaveRandom