• Publicidad

Capturar una única letra con expresión regular

¿Apenas comienzas con Perl? En este foro podrás encontrar y hacer preguntas básicas de Perl con respuestas aptas a tu nivel.

Capturar una única letra con expresión regular

Notapor pablgonz » 2015-08-10 23:14 @010

Hola a todos una vez más, vuelvo al foro con una pequeña duda respecto de una expresión regular.

Me explico: Tengo un archivo de texto el cual leo por medio de un script. Me gustaría hacer los siguientes cambios: si el texto de entrada contiene caracteres como estos:
Sintáxis: [ Descargar ] [ Ocultar ]
  1. \'{ A } \'A \=\i \`u \^e \^{e} \={\i} \'\i 

dejarlos de ésta manera:
Sintáxis: [ Descargar ] [ Ocultar ]
  1. \'{A} \'{A} \={\i} \`{u} \^{e} \^{e} \={\i} \'{\i} 
es decir, si tengo la combinación \ seguido de ',´,`,=,^,~, o . seguido de { y algunos espacios seguido y un \ seguido de una única letra aeiouAEOU seguido de uno o más espacios seguidos de }, dejarlas como \',´,`,=,^,~, o .{única letra} donde {}, espacios y \ pueden estar o no presentes.

He intentado con
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. s/(\\)(\'|\´|\`|\=|\^|\~|\"|\.)(\{\s*)?((\\)?[aeiouAEOU]){1}(\s*\})?/$1$2\{$4}/gms
Coloreado en 0.002 segundos, usando GeSHi 1.0.8.4
pero, no ha funcionado de manera correcta. Si ingreso
Sintáxis: [ Descargar ] [ Ocultar ]
  1. Quiero capturar \'{ A }bcde junto a \'Abcd o \=\ibcd, pero no \'{Aa} 

obtengo
Sintáxis: [ Descargar ] [ Ocultar ]
  1. Quiero capturar \'{A}bcde junto a \'{A}bcd o \={\i}bcd, pero no \'{A}a} 
y lo que quiero es
Sintáxis: [ Descargar ] [ Ocultar ]
  1. Quiero capturar \'{A}bcde junto a \'{A}bcd o \={\i}bcd, pero no \'{Aa} 

Agradecido de antemano,
Pablo
pablgonz
Perlero nuevo
Perlero nuevo
 
Mensajes: 236
Registrado: 2010-09-08 21:03 @919
Ubicación: Concepción (Chile)

Publicidad

Re: Capturar una única letra con expresión regular

Notapor explorer » 2015-08-11 04:57 @248

El problema está cuando marcamos como opcional la presencia de la llave de cierre: no tenemos en cuenta el caso de que detrás de la 'única letra' puede haber otra cosa distinta de espacios más llave de cierre (otras letras).

Entonces, se puede separar el problema en dos: tratarlo cuando hay llaves y cuando no.

Esta es una forma de hacerlo:
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. #!/usr/bin/env perl
  2. use feature 'say';
  3.  
  4. #my $texto = q(\'{ A } \'A \=\i \`u \^e \^{e} \={\i} \'\i );
  5. my $texto = q(Quiero capturar \'{ A }bcde junto a \'Abcd o \=\ibcd, pero no \'{Aa});
  6. say "[$texto]";
  7.  
  8. my $unicaletra = qr/(?<unicaletra>(?:\\)?[aeiouAEOU])/;
  9.  
  10. $texto =~ s
  11.    /
  12.         (\\['´`=^~.])                          # preámbulo
  13.         (?:                                     # agrupación de opciones
  14.             \{ \s* $unicaletra \s* \}           # primera opción, con llaves
  15.             |
  16.             $unicaletra                         # segunda opción, sin llaves
  17.         )
  18.  
  19.    /$1\{$+{unicaletra}\}/gx;
  20.  
  21. say "[$texto]";
  22. #say q([\'{A} \'{A} \={\i} \`{u} \^{e} \^{e} \={\i} \'{\i} ]);
  23. say q([Quiero capturar \'{A}bcde junto a \'{A}bcd o \={\i}bcd, pero no \'{Aa}]);
  24. __END__
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4

La solución consiste en poner las dos opciones, usando el operador de alternancia de patrones '|'.

Otra forma de hacerlo es viendo si realmente existe una llave de apertura, y en ese caso, obligar a que exista una llave de cierre:
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. #!/usr/bin/env perl
  2. use feature 'say';
  3.  
  4. #my $texto = q(\'{ A } \'A \=\i \`u \^e \^{e} \={\i} \'\i );
  5. my $texto = q(Quiero capturar \'{ A }bcde junto a \'Abcd o \=\ibcd, pero no \'{Aa});
  6. say "[$texto]";
  7.  
  8. $texto =~ s
  9.    /
  10.         (\\['´`=^~.])                          # preámbulo
  11.         (?<llave>   \{ \s*)?                    # caso de existir llave de apertura
  12.         (?<unicaletra>(?:\\)?[aeiouAEOU])       # la letra única
  13.         (?(<llave>) \s* \})                     # si existe llave de apertura, debe existir llave de cierre
  14.  
  15.    /$1\{$+{unicaletra}\}/gx;
  16.  
  17. say "[$texto]";
  18.  
  19. #say q([\'{A} \'{A} \={\i} \`{u} \^{e} \^{e} \={\i} \'{\i} ]);
  20. say q([Quiero capturar \'{A}bcde junto a \'{A}bcd o \={\i}bcd, pero no \'{Aa}]);
  21. __END__
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4

En este caso usamos una expresión regular extendida, la (?(condición)patrón si sí), que nos crea un subpatrón con la llave de cierre si existió la captura de la llave de apertura.

También hay que destacar el uso de grupos de captura con nombre (<unicaletra>). De esa manera, en la expresión de sustitución no tenemos que andar calculando si la letra estaba en el segundo o tercer grupo de captura (podría ser cualquiera de los dos ya que hemos marcado el grupo de captura de la llave de apertura como opcional).

He usado también la opción /x para poder desplegar el patrón por varias líneas y así poder comentarlas. La forma compacta sería así:
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. $texto =~ s/(\\['´`=^~.])(?<llave>\{\s*)?(?<unicaletra>(?:\\)?[aeiouAEOU])(?(<llave>)\s*\})/$1\{$+{unicaletra}\}/g;
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4
JF^D Perl programming & Raku programming. Grupo en Telegram: https://t.me/Perl_ES
Avatar de Usuario
explorer
Administrador
Administrador
 
Mensajes: 14480
Registrado: 2005-07-24 18:12 @800
Ubicación: Valladolid, España

Re: Capturar una única letra con expresión regular

Notapor pablgonz » 2015-08-11 22:37 @984

Muchas gracias, no había visto en acción a (?(condición)patrón si sí).

Una consulta (para variar): ¿Qué modificación se debe hacer a la expresión regular para que deje todos los casos de i de igual manera? Es decir, siguiendo las mismas condiciones de llaves y espacios. Si ingreso:
Sintáxis: [ Descargar ] [ Ocultar ]
Using text Syntax Highlighting
  1. \`i \'i \^i \~i \=i \`{i} \'{i} \^{i} \~{i} \={i} \`{\i} \'{\i} \^{\i} \~{\i} \={\i}
Coloreado en 0.000 segundos, usando GeSHi 1.0.8.4

que salgan todas las de la forma \'`=~.{\i}.

En realidad, solo la letra i puede llevar un \ antes, y me gustaría que todas quedaran de igual manera. Usaré un hash en otra parte del script y eso la haría más cómodo. En fin, solo si es posible.

Agradecido,
Pablo
pablgonz
Perlero nuevo
Perlero nuevo
 
Mensajes: 236
Registrado: 2010-09-08 21:03 @919
Ubicación: Concepción (Chile)

Re: Capturar una única letra con expresión regular

Notapor explorer » 2015-08-11 23:07 @005

Bueno, se puede modificar algo:
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. #!/usr/bin/env perl
  2. use feature 'say';
  3.  
  4. my $texto = q(\`i \'i \^i \~i \=i \`{i} \'{i} \^{i} \~{i} \={i} \`{\i} \'{\i} \^{\i} \~{\i} \={\i});
  5. say $texto;
  6.  
  7. $texto =~ s/
  8.         (\\['´`=^~.])                          # preámbulo
  9.         (?<llave>   \{ \s*)?                    # caso de existir llave inicial
  10.         (?<unicaletra>(?:\\i|[aeiouAEOU]))      # la letra única: o es '\i' o es una letra
  11.         (?(<llave>) \s* \})                     # si existe llave inicial, debe existir llave final
  12.  
  13.     /"$1\{" . ($+{unicaletra} eq 'i' ? '\i' : $+{unicaletra}) . "\}"/gex;
  14.  
  15. say q(\`{\i} \'{\i} \^{\i} \~{\i} \={\i} \`{\i} \'{\i} \^{\i} \~{\i} \={\i} \`{\i} \'{\i} \^{\i} \~{\i} \={\i});
  16. say $texto;
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4
Sale:
Sintáxis: [ Descargar ] [ Ocultar ]
Using text Syntax Highlighting
\`i \'i \^i \~i \=i \`{i} \'{i} \^{i} \~{i} \={i} \`{\i} \'{\i} \^{\i} \~{\i} \={\i}
\`{\i} \'{\i} \^{\i} \~{\i} \={\i} \`{\i} \'{\i} \^{\i} \~{\i} \={\i} \`{\i} \'{\i} \^{\i} \~{\i} \={\i}
\`{\i} \'{\i} \^{\i} \~{\i} \={\i} \`{\i} \'{\i} \^{\i} \~{\i} \={\i} \`{\i} \'{\i} \^{\i} \~{\i} \={\i}
Coloreado en 0.000 segundos, usando GeSHi 1.0.8.4

Se ha cambiado el patrón para obligar a que solo '\' preceda a una 'i'.

Y en la segunda parte, en la sustitución, la hemos cambiado por un código Perl ejecutable, y ahí preguntamos (con el operador ternario) si la única letra es una solitaria 'i', y en ese caso le añadimos '\' inicial.
JF^D Perl programming & Raku programming. Grupo en Telegram: https://t.me/Perl_ES
Avatar de Usuario
explorer
Administrador
Administrador
 
Mensajes: 14480
Registrado: 2005-07-24 18:12 @800
Ubicación: Valladolid, España

Re: Capturar una única letra con expresión regular

Notapor pablgonz » 2015-08-12 07:40 @361

Genial, ahora sale justo como lo deseaba.

Asumo que si en el futuro agrego otro, por ejemplo la j, la cual se comporta la i (pero es muy poco frecuente el que aparezca en mis archivos de entrada), bastará con cambiar en el lado izquierdo de la expresión regular a:
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. (?<unicaletra>(?:\\i|[aeiouAEOU]))
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4

por
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. (?<unicaletra>(?:\\(i|j)|[aeiouAEOU]))
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4

y en el lado derecho cambiar de:
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. /"$1\{" . ($+{unicaletra} eq 'i' ? '\i' : $+{unicaletra}) . "\}"/gex;
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4

a:
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. /"$1\{" . ($+{unicaletra} eq 'i' or 'j' ? '\i' ? '\j': $+{unicaletra}) . "\}"/gex;
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4

(en mi mente funciona).
Saludos
pablgonz
Perlero nuevo
Perlero nuevo
 
Mensajes: 236
Registrado: 2010-09-08 21:03 @919
Ubicación: Concepción (Chile)

Re: Capturar una única letra con expresión regular

Notapor explorer » 2015-08-12 08:11 @383

No es tan sencillo...

Propongo un cambio de estrategia: sacamos a la 'i' y a la 'j' de la lista de letras únicas, y las ponemos como un caso especial, en donde pueden estar o no precedidas por '\'. En caso de existir el caso especial, obligamos a poner siempre el '\'.

Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. #!/usr/bin/env perl
  2. use feature 'say';
  3.  
  4. my $texto = q(\'{ A } \'A \=\i \`u \^e \^{e} \={\i} \'\i
  5. \`i \'i \^i \~i \=i \`{i} \'{i} \^{i} \~{i} \={i} \`{\i} \'{\i} \^{\i} \~{\i} \={\i}
  6. \`j \'j \^j \~j \=j \`{j} \'{j} \^{j} \~{j} \={j} \`{\j} \'{\j} \^{\j} \~{\j} \={\j});
  7. say $texto;
  8.  
  9. my $preambulo  = qr/(?<preambulo>\\['´`=^~.])/;                        # preámbulo
  10. my $especiales = qr/\\?(?<especiales>[ij])/;                    # caracteres con trato especial
  11. my $letraunica = qr/(?<letraunica>[aeouAEOU])/;                 # letras que aparecen solas
  12.  
  13. $texto =~ s/
  14.         $preambulo                      # preámbulo
  15.         (?<llave>   \{ \s*)?            # caso de existir llave inicial
  16.         (?:$especiales|$letraunica)     # la letra única: o es un caso especial o es una letra sola
  17.         (?(<llave>) \s* \})             # si existe llave inicial, debe existir llave final
  18.  
  19.    /"$+{preambulo}\{" . ($+{especiales} ? "\\$+{especiales}" : $+{letraunica}) . "\}"/gex;
  20.  
  21. say;
  22. say q(\'{A} \'{A} \={\i} \`{u} \^{e} \^{e} \={\i} \'{\i}
  23. \`{\i} \'{\i} \^{\i} \~{\i} \={\i} \`{\i} \'{\i} \^{\i} \~{\i} \={\i} \`{\i} \'{\i} \^{\i} \~{\i} \={\i}
  24. \`{\j} \'{\j} \^{\j} \~{\j} \={\j} \`{\j} \'{\j} \^{\j} \~{\j} \={\j} \`{\j} \'{\j} \^{\j} \~{\j} \={\j});
  25. say;
  26. say $texto;
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4
JF^D Perl programming & Raku programming. Grupo en Telegram: https://t.me/Perl_ES
Avatar de Usuario
explorer
Administrador
Administrador
 
Mensajes: 14480
Registrado: 2005-07-24 18:12 @800
Ubicación: Valladolid, España

Re: Capturar una única letra con expresión regular

Notapor pablgonz » 2015-08-12 09:15 @427

Pensé que bastaría con cambios como esos para abarcar el caso de la j... :(

Al escribir no le tomé importancia al caso de la j. Luego de enviar la respuesta al foro me quedé pensando en los casos de la j, y claro, intenté hacer algunos cambios y no me funcionaba.

El cambio de estrategia que propones es perfecto... Abarca todos los casos (la i y la j son las únicas con trato especial).

Agradecido.
Pablo
pablgonz
Perlero nuevo
Perlero nuevo
 
Mensajes: 236
Registrado: 2010-09-08 21:03 @919
Ubicación: Concepción (Chile)

Re: Capturar una única letra con expresión regular

Notapor explorer » 2015-08-13 20:21 @890

Se podría intentar poniendo algo como esto:
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. /"$1\{" . ($+{unicaletra} eq 'i'  ? '\i' : $+{unicaletra} eq 'j' ? '\j' : $+{unicaletra}) . "\}"/gex;
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4

pero es más difícil de mantener que la línea que propongo ahora:
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. my $especiales = qr/\\?(?<especiales>[ij])/;
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4
JF^D Perl programming & Raku programming. Grupo en Telegram: https://t.me/Perl_ES
Avatar de Usuario
explorer
Administrador
Administrador
 
Mensajes: 14480
Registrado: 2005-07-24 18:12 @800
Ubicación: Valladolid, España


Volver a Básico

¿Quién está conectado?

Usuarios navegando por este Foro: No hay usuarios registrados visitando el Foro y 1 invitado