• Publicidad

Expresión regular de matching

Así que programas sin strict y las expresiones regulares son otro modo de hablar. Aquí encontrarás respuestas de nivel avanzado, no recomendable para los débiles de corazón.

Expresión regular de matching

Notapor noballack » 2006-11-02 10:21 @473

Hola a todos, este es mi primer post en el foro, hace unos días que me estoy peleando con una expresion regular que tiene que hacer un match y no encuentro ninguna solución, quizás es que no existe ni idea, a ver si me podeis echar un cable.

El problema:
Tengo esta expresión regular:

Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
$url =~ /<a href="([^\"]+)">([^\>]+)</a>/;
print "[$1] [$2]";
Coloreado en 0.002 segundos, usando GeSHi 1.0.8.4


El funcionaminto es el siguiente:

Código: Seleccionar todo
ejemplo 1: si aplicamos la expresión regular a la cadena $url
                 $url = '<a href="/story/43480/">The Bottled Water Lie</a>';

                 obtenemos el resultado siguiente:
                 $1 vale /story/43480/
                 $2 vale The Bottled Water Lie.

ejemplo2:  si aplicamos la expresión regular a la cadena $url
                 $url = '<a href="/story"/43480/">The Bottled Water Lie</a>';

                 obtenemos el resultado siguiente:
                 $1 vale '' (vacío)
                 $2 vale '' (vacío)


Necesito una modificación sobre la expresión regular original que en el caso de que exista la palabra 'video' dentro de la url no devuelva nada y si no existe devuelva lo mismo que la expresión del ejemplo 1.

Código: Seleccionar todo
ejemplo 3: $url = '<a href="/story/video/43480/">The Bottled Water Lie</a>';

                 debe devolver:
                 $1 vale '' (vacío)
                 $2 vale '' (vacío)


Muchas gracias, para mi que no hay respuesta pero bueno, si me podeis ayudar me hareis un gran favor.
noballack
Perlero nuevo
Perlero nuevo
 
Mensajes: 9
Registrado: 2006-11-02 10:09 @464

Publicidad

Notapor explorer » 2006-11-02 10:35 @483

En principio, veo que en el ejemplo2 hay unas dobles comillas después de la palabra story, por lo que entonces falla la expresión regular y devuelve vació en las dos variables.
JF^D Perl programming & Raku programming. Grupo en Telegram: https://t.me/Perl_ES
Avatar de Usuario
explorer
Administrador
Administrador
 
Mensajes: 14486
Registrado: 2005-07-24 18:12 @800
Ubicación: Valladolid, España

Notapor noballack » 2006-11-02 10:46 @490

La expresión original esta funcionando correctamente, quiero decir los ejemplos 1 y 2 son para ilustrar el funcionamiento actual de la expresión.

La expresión devuelve match en el caso que no haya unas comillas y no devuelve nada en el caso que aparezcan unas comillas, actualmente se esta utilizando para verificar que no aparezcan estas dichosas comillas, pero yo necesito que haga algo mas. Necesito que ademas verifique que no aparezca la palabra 'video' y necesito hacerlo modificando la expresión original, no puedo usar otra expresión complementaria. En una sola expresión necesito verificar si sale la palabra video o no y si salen las comillas o no. O sea debe devolver match en el caso que no haya ni comillas ni la palabra 'video'
noballack
Perlero nuevo
Perlero nuevo
 
Mensajes: 9
Registrado: 2006-11-02 10:09 @464

Notapor Perl user » 2006-11-02 11:26 @518

Que tal,

Te recomiendo antes de continuar, que le des un vistazo al módulo URI, es siempre 10000% recomendable que a menos que conozcas realmente el formato REAL de una URL, no intentes parsearlo tu mismo, mejor utiliza el módulo, que lo hace por tí y lo hace bien, con cualquiera de los casos extremos, de esa manera no tendrás que lidiar con bugs en alguna URL que no siga el formato que tu estás especificando (incluyendo mal formadas).

Saludos,
Marco A. Manzo
[email protected]
http://www.unixmonkeys.com/amnesiac/
Perl Programming Language
Perl user
Maestro honorario
Maestro honorario
 
Mensajes: 271
Registrado: 2004-11-03 21:11 @924

Notapor noballack » 2006-11-02 11:35 @524

El problema es que no estoy intentando capturar una url, estoy intentando capturar una cadena de caracteres cualquiera que no contenga la palabra 'video' el texto del ejemplo es sólo eso, un texto de ejemplo.

p. ej.:
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
$cadena = 'la madre "de josé me" vuelve loco';

$cadena =~ /"([^\"]+)"/;
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4


esto devolvería el resultado $1 igual a 'de josé me';

Lo que yo quiero es que si existe la palabra video dentro de las comillas devuelva nulo, pero que si no existe devuelva lo que hay entre las comillas.
noballack
Perlero nuevo
Perlero nuevo
 
Mensajes: 9
Registrado: 2006-11-02 10:09 @464

Notapor Perl user » 2006-11-02 11:55 @538

Que tal,

Necesitas la expresión:

Código: Seleccionar todo
/"(?!video)([^"]+)"/


Es necesario siempre echarle un vistazo a los manuales perlre, perlretut y perlrequick, ¿ok? Busca por look-behind y look-ahead.

Saludos,
Marco A. Manzo
[email protected]
http://www.unixmonkeys.com/amnesiac/
Perl Programming Language
Perl user
Maestro honorario
Maestro honorario
 
Mensajes: 271
Registrado: 2004-11-03 21:11 @924

Notapor explorer » 2006-11-02 13:10 @590

Lo he intentado hacer con '(?!video)', pero no lo he conseguido.

El problema es que no se dice en qué parte de la cadena de caracteres capturada por el primer par de paréntesis se debe encontrar la palabra 'video'. Se supone entonces que puede aparecer en cualquier parte.

De hecho, el ejemplo indicado por Perl User está marcado como posible error en la explicación de look-ahead, que es justo lo que '(?!...)' indica. Así, '/"(?!video)([^"]+)"/' estamos queriendo decir que lo siguiente a '(?!video)' no ha de ser 'video'. Y, efectivamente, '([^"]+)' es muy probable que no comience con 'video'. No sabemos realmente en qué posición puede estar ese 'video'. Si supiéramos que está en un segundo nivel de directorios, por ejemplo, sí que se podría hacer una expresión regular para ello.

Lo mismo se puede aplicar para look-behind. No sabemos cuántos caracteres pueden existir entre 'video' y la parte anterior.

La recomendación entonces es de hacerlo lo más sencillo posible, claro...

Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
if ( $url =~ m{href="(.+)">(.+)</a>}   and   $1 !~ /video/ ) {
    print "1: $1\n";
    print "2: $2\n";
}
else {
    print "1: $1\n";
    print "2: $2\n";
}
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4
No es una única expresión regular, pero al menos vemos muy claro qué es lo que estamos buscando.

Código: Seleccionar todo
$url = '<a href="/story/43480/">The Bottled Water Lie</a>';
   devuelve
   1: /story/43480/
   2: The Bottled Water Lie

$url = '<a href="/story/video/43480/">The Bottled Water Lie</a>';
   devuelve
   1:
   2:


Fíjate que no usamos lo de '[^"]' ni '[^<]' porque lo que rodea a los paréntesis de captura es lo suficientemente expresivo como para que Perl sepa si debe 'encajar' nuestra $url con ese patrón. La clave está en los caracteres fijos que sabemos que sí existen. Sólo tendríamos problemas si $url estuviera compuesta de más de un URL (en la misma línea).

Además, en la parte del 'else', tanto $1 como $2 están indefinidos, que es lo que pedías.

Si quieres dejarlo en una línea:
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
$url =~ m{href="(.+)">(.+)</a>}   and   $1 !~ /video/;
print "[$1] [$2]\n";
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4


(tampoco me sorprendería saber que sí se puede hacer con una única expresión regular, pero me temo que podría ser tan larga como la de comprobar si un correo electrónico está correctamente escrito :-))
JF^D Perl programming & Raku programming. Grupo en Telegram: https://t.me/Perl_ES
Avatar de Usuario
explorer
Administrador
Administrador
 
Mensajes: 14486
Registrado: 2005-07-24 18:12 @800
Ubicación: Valladolid, España

Notapor Perl user » 2006-11-02 13:39 @610

explorer escribiste:De hecho, el ejemplo indicado por Perl User está marcado como posible error en la explicación de look-ahead, que es justo lo que '(?!...)' indica. Así, '/"(?!video)([^"]+)"/' estamos queriendo decir que lo siguiente a '(?!video)' no ha de ser 'video'. Y, efectivamente, '([^"]+)' es muy probable que no comience con 'video'. No sabemos realmente en qué posición puede estar ese 'video'.


Que tal,

No, de hecho eso no es lo que quiere decir un look-ahead. Un look-ahead es una simple verificación, no es un matching como tal. Lo que quiere decir el look-ahead negativo es, que la palabra video NO se encuentre después del patrón anterior ( en este caso de las comillas ), recuerda, el manual dice claramente que es un 'assertion'.
Nuevamente, el look-ahead que puse no lo estoy poniendo sobre el patrónn [^"], sino sobre las primeras comillas.

El patrón es válido, de hecho lo he probado y sigue funcionando para mi.

Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
!/usr/bin/perl

use strict;
use warnings;

# intenta cambiar la palabra amigo por cualquier video y viceversa
my $str = 'La hermana de mi "amigo" me gusta';

print $1 if $str =~ /"(?!video)([^"]+)"/;
 
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4




explorer escribiste:Lo mismo se puede aplicar para look-behind. No sabemos cuántos caracteres pueden existir entre 'video' y la parte anterior.

La recomendación entonces es de hacerlo lo más sencillo posible, claro...

Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
if ( $url =~ m{href="(.+)">(.+)</a>}   and   $1 !~ /video/ ) {
    print "1: $1\n";
    print "2: $2\n";
}
else {
    print "1: $1\n";
    print "2: $2\n";
}
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4
No es una única expresión regular, pero al menos vemos muy claro qué es lo que estamos buscando.

Código: Seleccionar todo
$url = '<a href="/story/43480/">The Bottled Water Lie</a>';
   devuelve
   1: /story/43480/
   2: The Bottled Water Lie

$url = '<a href="/story/video/43480/">The Bottled Water Lie</a>';
   devuelve
   1:
   2:


Fíjate que no usamos lo de '[^"]' ni '[^<]' porque lo que rodea a los paréntesis de captura es lo suficientemente expresivo como para que Perl sepa si debe 'encajar' nuestra $url con ese patrón. La clave está en los caracteres fijos que sabemos que sí existen. Sólo tendríamos problemas si $url estuviera compuesta de más de un URL (en la misma línea).

Además, en la parte del 'else', tanto $1 como $2 están indefinidos, que es lo que pedías.

(tampoco me sorprendería saber que sí se puede hacer con una única expresión regular, pero me temo que podría ser tan larga como la de comprobar si un correo electrónico está correctamente escrito :-))


Si, dicha solución es correcta, pero obviamente este problema es trivial, y es posible solucionarlo sin muchas verificaciones, aparte de que no, no creo que se compare con el problema de verificar un e-mail.

Saludos,
Marco A. Manzo
[email protected]
http://www.unixmonkeys.com/amnesiac/
Perl Programming Language
Perl user
Maestro honorario
Maestro honorario
 
Mensajes: 271
Registrado: 2004-11-03 21:11 @924

Misterio

Notapor explorer » 2006-11-02 21:43 @947

A propósito... la siguiente línea es un poco especial...
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
$url =~ m{href="(.+)">(.+)</a>}   and   $1 !~ /video/;
print "[$1] [$2]\n";
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4

Ya que estamos en el foro de Experto, es interesante ver un hecho curioso...

¿Cómo funciona esa línea?

¿Algún experto se atreve a decir cómo y porqué funciona?

Yo llevo más de dos horas pensando y aún no lo he conseguido. Sobre todo después de leer estos dos párrafos en perlre:
The numbered match variables ($1, $2, $3, etc.) and the related punctuation set ($+ , $& , $` , $' , and $^N ) are all dynamically scoped until the end of the enclosing block or until the next successful match, whichever comes first. (See "Compound Statements" in perlsyn.)

NOTE: failed matches in Perl do not reset the match variables, which makes it easier to write code that tests for a series of more specific cases and remembers the best match.

Es decir... ¿Por qué se resetean las variables numéricas $1 y $2 en el fallo de la segunda condición si perlre dice que no es lo que debe de ocurrir...?
JF^D Perl programming & Raku programming. Grupo en Telegram: https://t.me/Perl_ES
Avatar de Usuario
explorer
Administrador
Administrador
 
Mensajes: 14486
Registrado: 2005-07-24 18:12 @800
Ubicación: Valladolid, España

Notapor explorer » 2006-11-03 08:34 @399

Bueno, pues con la ayuda de ikegami se ha resuelto el misterio:

$1 !~ /video/ en realidad es !( $1 =~ /video/ ), por lo que, en el segundo $url, al encontrarse efectivamente con la palabra video, las variables $1 y $2 son puestas a indefinido porque en el patrón no hay paréntesis de captura. Y el '!' inicial niega el resultado de la búsqueda. El resultado del 'and' es simplemente ejecutar una instrucción que hay dentro del 'else', o la de ejecutar una instrucción tras otra en el caso de la opción de una sola línea.

Y, finalmente, ha encontrado una expresión regular que resuelve el problema de noballack:

Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
$url =~ m{href="((?:(?!video).)+)">(.+)</a>}
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4

Lo he probado y funciona... pero no estoy seguro de porqué... yo creo que debería ser
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
$url =~ m{href="((?:(?<!video).)+)">(.+)</a>}
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4
que también funciona.

http://www.perlmonks.org/index.pl?node_id=581996
JF^D Perl programming & Raku programming. Grupo en Telegram: https://t.me/Perl_ES
Avatar de Usuario
explorer
Administrador
Administrador
 
Mensajes: 14486
Registrado: 2005-07-24 18:12 @800
Ubicación: Valladolid, España

Siguiente

Volver a Avanzado

¿Quién está conectado?

Usuarios navegando por este Foro: No hay usuarios registrados visitando el Foro y 0 invitados