2010-07-20 41 views
5

Estoy bien con expresiones regulares básicas, pero me pierdo un poco en pos/neg look aheads/behinds.usando regex para omitir todos los caracteres hasta que se encuentre una secuencia específica de letras usando lookahead negativo

Estoy tratando de tirar de la ID # de esto:

[keyword stuff=otherstuff id=123 morestuff=stuff]

Podría haber una cantidad ilimitada de "cosas" antes o después. He estado usando la expresión regular entrenador para ayudar a depurar lo que he intentado, pero no voy a avanzar más ...

Hasta ahora tengo esto:

\[keyword (?:id=([0-9]+))?[^\]]*\] 

que se encarga de cualquier atributo adicional después de la identificación, pero no puedo encontrar la manera de ignorar todo entre la palabra clave y la identificación. Sé que no puedo ir [^id]* Creo que necesito usar un lookahead negativo como este (?!id)* pero supongo que como es de ancho cero, no avanza desde allí. Esto no funciona bien:

\[keyword[A-z0-9 =]*(?!id)(?:id=([0-9]+))?[^\]]*\] 

He estado buscando por todas partes ejemplos, pero no he encontrado ninguna. O tal vez lo hice, pero fueron tan lejos de mi cabeza que ni siquiera me di cuenta de lo que eran.

¡Ayuda! Gracias.

EDIT: También tiene que coincidir con [palabra clave stuff = otherstuff], donde id = no existe en absoluto, así que tengo que tener un 1 o 0 en el grupo id #. También hay otras [otherkeywords id = 32] con las que no quiero hacer coincidir. El documento debe coincidir con varios [id. De palabras clave = 3] en todos los documentos utilizando preg_match_all.

+0

Las soluciones proporcionadas funcionan muy bien y probablemente se comparen más rápido que con cualquier tipo de búsqueda anticipada, definitivamente lo haré de esa manera. Pero para mi propia curiosidad, y tal vez cualquier persona que acierta esta pregunta con google en un futuro lejano, ¿es el método que intenté lograr? Es decir, ¿se puede usar lookaheads para saltear algunas cosas hasta que se golpee una palabra en particular? – phazei

Respuesta

2

Sin búsqueda hacia delante/detrás requerido:

/\[keyword(?:[^\]]*?\bid=([0-9]+))?[^\]]*?\]/ 

añade la terminación '[^]] *]' para comprobar si hay un extremo de la etiqueta real, podría ser innecesario.

Editar: se ha añadido la \ b para ello como de lo contrario podría coincidir [keyword you-dont-want-this-guid=123123-132123-123 id=123]

$ php -r 'preg_match_all("/\[keyword(?:[^\]]*?\bid=([0-9]+))?[^\]]*?\]/","[keyword stuff=otherstuff morestuff=stuff]",$matches);var_dump($matches);' 
array(2) { 
    [0]=> 
    array(1) { 
    [0]=> 
    string(42) "[keyword stuff=otherstuff morestuff=stuff]" 
    } 
    [1]=> 
    array(1) { 
    [0]=> 
    string(0) "" 
    } 
} 
$ php -r 'var_dump(preg_match_all("/\[keyword(?:[^\]]*?\bid=([0-9]+))?[^\]]*?\]/","[keyword stuff=otherstuff id=123 morestuff=stuff]",$matches),$matches);' 
int(1) 
array(2) { 
    [0]=> 
    array(1) { 
    [0]=> 
    string(49) "[keyword stuff=otherstuff id=123 morestuff=stuff]" 
    } 
    [1]=> 
    array(1) { 
    [0]=> 
    string(3) "123" 
    } 
} 
+0

Estaba pensando que funcionaba, pero después de probarlo, parece que id no es opcional y debe serlo. – phazei

+0

Oh, no lo entendí, lo arreglaré, – Wrikken

+0

Solucionado (en un subpatrón no captor) – Wrikken

2

No es necesario mirar hacia adelante/atrás.

Dado que la pregunta está etiquetada PHP, use preg_match_all() y almacene la partida en $ matches.

Así es como:

<?php 

    // Store the string. I single quote, in case there are backslashes I 
    // didn't see. 
$string = 'blah blah[keyword stuff=otherstuff id=123 morestuff=stuff] 
      blah blah[otherkeyword stuff=otherstuff id=555 morestuff=stuff] 
      blah blah[keyword stuff=otherstuff id=444 morestuff=stuff]'; 

    // The pattern is '[keyword' followed by not ']' a space and id 
    // The space before id is important, so you don't catch 'guid', etc. 
    // If '[keyword' is always at the beginning of a line, you can use 
    // '^\[keyword' 
$pattern = '/\[keyword[^\]]* id=([0-9]+)/'; 

    // Find every single $pattern in $string and store it in $matches 
preg_match_all($pattern, $string, $matches); 

    // The only tricky part you have to know is that each entire match is stored in 
    // $matches[0][x], and the part of the match in the parentheses, which is what 
    // you want is stored in $matches[1][x]. The brackets are optional, since it's 
    // only one line. 
foreach($matches[1] as $value) 
{  
    echo $value . "<br/>"; 
} 
?> 

Salida:

123 
444 

(555 se omite, como debe ser)

PS

también puede utilizar \b vez de un espacio literal si el re podría ser una pestaña en su lugar. \b represents a word boundary ... en este caso, el comienzo de una palabra.

$pattern = '/\[keyword[^\]]*\bid=([0-9]+)/'; 
+0

Eso no funcionará, porque estoy usando preg_match_all en un documento grande que podría tener [otherkeyword id = 324] que no puedo emparejar. Además, tengo que hacer coincidir [palabra clave stuff = otherstuff] donde no hay id. – phazei

+0

@phazei Edité mi respuesta para mostrar varias respuestas e ignoro otherkeyword. –

+0

Cool. Se salteó todo después de la identificación, aunque debo conservarlo ya que lo estoy usando para reemplazar toda la sección [palabra clave x = x], pero ese no es un problema para mí. Veo que resolvió el problema más importante que estaba teniendo de la misma manera que Wrikken lo hizo con [^]] * justo después de la palabra clave. ¿Por qué funciona eso y no causa que se salte todo hasta el último "]"? – phazei

0

Creo que esto es lo que está recibiendo en:

\[keyword(?:\s+(?!id\b)[A-Za-z]+=[^\]\s]+)*(?:\s+id=([0-9]+))?[^\]]*\] 

(Asumo nombres de atributo sólo puede contener letras ASCII, mientras que los valores pueden contener cualquier carácter no está en blanco, excepto ] .)

(?:\s+(?!id\b)[A-Za-z]+=[^\]\s]+)* coincide con cualquier número de pares attribute=value (y el espacio en blanco que los precede), siempre que el nombre del atributo no sea id. El \b (límite de palabras) está allí en caso de que haya nombres de atributos que comiencen con id, como idiocy. No hay necesidad de poner \bfrente a el nombre del atributo esta vez, porque sabe que cualquier nombre que coincida estará precedido por espacios en blanco. Pero, como has aprendido, el enfoque de anticipación es excesivo en este caso.

Ahora, en esto:

[A-z0-9 =] 

Eso es A-z ya sea un error tipográfico o un error. Si espera que coincida con todas las letras mayúsculas y minúsculas, bueno, lo hace. Pero también coincide con

'[', ']', '^', '_', '`` and '\' 

... porque sus puntos de código se encuentran entre los de las letras mayúsculas y minúsculas. Cartas ASCII, eso es.

Cuestiones relacionadas