• Publicidad

Modificar texto con expresión regular anidada

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

Modificar texto con expresión regular anidada

Notapor pablgonz » 2019-09-19 21:48 @950

Hola, estoy tratando de capturar y eliminar algunos bloques de texto que se encuentran anidados dentro de llaves {...} las cuales están correctamente balanceadas, y dentro de estas se encuentran llaves escapadas \{ ...\} que no siempre están balanceadas. El script que poseo es el siguiente:
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. #!/usr/bin/env perl
  2. use v5.28;
  3. use autodie;
  4. use File::Basename;
  5.  
  6. ## Args
  7. @ARGV == 1  or die "Uso: $0 <archivo TeX to process>\n";
  8. my $nombre_archivo = shift;
  9. -f $nombre_archivo or die "ERROR: No encuentro [$nombre_archivo]\n";
  10.  
  11. ## Extensión
  12. my @SuffixList = ('.tex', '', '.ltx');
  13. my ($name, $path, $ext) = fileparse($nombre_archivo, @SuffixList);
  14. $ext = '.tex' if not $ext;
  15.  
  16. ## Lectura
  17. open my $ENTRADA, '<', $nombre_archivo;
  18. my $archivo;
  19.  {
  20.     local $/;
  21.     $archivo = <$ENTRADA>;
  22.  }
  23. close   $ENTRADA;
  24.  
  25. ## Regex
  26. my $anidado  = qr/(\{(?>[^\{\}\\]++|\\.|(?R))*+\})           /x;
  27. my $ifthen   = qr/\\ (?:ifthenelse\{\\boolean\{techrep\}\})  /x;
  28.  
  29. $archivo =~ s/(?:$ifthen)($anidado)(?:$anidado)/STARTRM$1STOPRM/gmsx;
  30. $archivo =~ s/(?:STARTRM\{)(.+?)(?:\}STOPRM)/$1/gms;
  31.  
  32. ## Escritura
  33. open my $SALIDA, '>', "$name-out$ext";
  34. print   $SALIDA $archivo;
  35. close   $SALIDA;
  36.  
  37. __END__
Coloreado en 0.004 segundos, usando GeSHi 1.0.8.4


Si el archivo de entrada es de la siguiente forma:
Sintáxis: [ Descargar ] [ Ocultar ]
Using latex Syntax Highlighting
  1. \documentclass{article}
  2. \newboolean{techrep}
  3. \setboolean{techrep}{false}
  4. \begin{document}
  5. \ifthenelse{\boolean{techrep}}{Este es texto
  6. con el que \{deseo quedarme\} incluidas las llaves \{ \} que no están
  7. escapadas dentro de el, pero sin las llaves exteriores
  8. }{Este es texto NO lo deseo, también puede llevar llaves \{ \}
  9. que no están escapadas} %
  10. TEXTO TEXTO
  11. TEXTO TEXTO \ifthenelse{\boolean{techrep}}{Otro texto
  12. con el que \{deseo quedarme\} incluidas las llaves \{ \}
  13. que no están escapadas
  14. }{Este es texto NO lo deseo, también puede llevar llaves \{ \}
  15. que no están escapadas}
  16. TEXTO TEXTO TEXTO
  17. \end{document}
Coloreado en 0.002 segundos, usando GeSHi 1.0.8.4

Obtengo de salida (que es más menos lo que busco):
Sintáxis: [ Descargar ] [ Ocultar ]
Using latex Syntax Highlighting
  1. \documentclass{article}
  2. \newboolean{techrep}
  3. \setboolean{techrep}{false}
  4. \begin{document}
  5. Este es texto
  6. con el que \{deseo quedarme\} incluidas las llaves \{ \} que no están
  7. escapadas dentro de el, pero sin las llaves exteriores
  8.  %
  9. TEXTO TEXTO
  10. TEXTO TEXTO Otro texto
  11. con el que \{deseo quedarme\} incluidas las llaves \{ \}
  12. que no están escapadas
  13.  
  14. TEXTO TEXTO TEXTO
  15. \end{document}
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4

Pero si el archivo de entrada es de la siguiente forma:
Sintáxis: [ Descargar ] [ Ocultar ]
Using latex Syntax Highlighting
  1. \documentclass{article}
  2. \newboolean{techrep}
  3. \setboolean{techrep}{false}
  4. \begin{document}
  5. \ifthenelse{\boolean{techrep}}{Este es texto
  6. con el que {deseo quedarme} incluidas las llaves \{ que no están
  7. escapadas dentro de el, pero sin las llaves exteriores
  8. }{Este es texto NO lo deseo, también {puede llevar llaves}  \}
  9. que no están escapadas} %
  10. TEXTO TEXTO
  11. TEXTO TEXTO \ifthenelse{\boolean{techrep}}{Otro texto
  12. con el que \{deseo quedarme\} incluidas las llaves \{ \}
  13. que no están escapadas
  14. }{Este es texto {NO lo deseo}, también puede llevar llaves \{
  15. que no están \} escapadas}
  16. TEXTO TEXTO TEXTO
  17. \end{document}
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4

Esperaría que la salida fuera:
Sintáxis: [ Descargar ] [ Ocultar ]
Using latex Syntax Highlighting
  1. \documentclass{article}
  2. \newboolean{techrep}
  3. \setboolean{techrep}{false}
  4. \begin{document}
  5. Este es texto
  6. con el que {deseo quedarme} incluidas las llaves \{ que no están
  7. escapadas dentro de el, pero sin las llaves exteriores %
  8. TEXTO TEXTO
  9. TEXTO TEXTO Otro texto
  10. con el que \{deseo quedarme\} incluidas las llaves \{ \}
  11. que no están escapadas
  12. TEXTO TEXTO TEXTO
  13. \end{document}
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4


¿Puedo hacer esto usando solo expresiones regulares? o ¿puedo hacerlo de otra manera?
Saludos
pablgonz
Perlero nuevo
Perlero nuevo
 
Mensajes: 236
Registrado: 2010-09-08 21:03 @919
Ubicación: Concepción (Chile)

Publicidad

Re: Modificar texto con expresión regular anidada

Notapor explorer » 2019-09-22 15:11 @674

Sí, sí que se puede realizar con expresiones regulares, sin problemas.

Bueno... sí que hay un problema que lo complica bastante: la presencia de las llaves escapadas.

Se podría... intentar hacer una exp. reg. que las encuentre, dentro de la exp. reg. general, pero hay una solución más sencilla.

Lo que podemos hacer es hacer una sustitución previa, de cada presencia de "\{" y "\}" por caracteres que "sabemos" que no van a estar en el resto del documento. Yo he elegido "«" y "»". Al final de toda la transformación, volvemos a sustituir estos caracteres por las llaves escapadas.

Entonces, el siguiente programa, da la salida esperada:
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. #!/usr/bin/env perl
  2. use v5.26;
  3. use utf8;
  4. use autodie;
  5. use open ':locale';
  6. #use re "debugcolor";
  7.  
  8. # Leemos el archivo
  9. #
  10. open my $ARCHIVO, '<', 'kk.txt';
  11. my $archivo = join '', <$ARCHIVO>;
  12. close $ARCHIVO;
  13.  
  14. # Cambiamos las llaves escapadas por un carácter que sabemos que no está en el resto del documento
  15. #
  16. $archivo =~ s/\\[{]/«/g;
  17. $archivo =~ s/\\[}]/»/g;
  18.  
  19. # Definición de la expresión regular.
  20. # Hay DEFINE donde definimos los distintos patrones que queremos encontrar.
  21. # Las tres principales, la condición, la exp. verdadera y la falsa, se basan en lo mismo: una secuencia de
  22. # texto que está rodeada por llaves, que pueden contener más llaves incluidas.
  23. # El patrón texto_rx es el encargado de buscar llaves emparejadas, y llaves incrustadas.
  24. # Encima del DEFINE está el patrón de búsqueda, que hace uso de los patrones anteriores.
  25. #
  26. # Solo hay dos grupos de capturas: el ifthenelse completo, y expVerdadero
  27. #
  28. my $ifthenelse = qr/
  29.  
  30.     (
  31.         \\ifthenelse (?&condicion_rx)
  32.             (?<expVerdadero> (?&expVerdadero_rx))
  33.             (?&expFalsa_rx)
  34.     )
  35.  
  36.     (?(DEFINE)
  37.         (?<condicion_rx>    (?&texto_rx))
  38.         (?<expVerdadero_rx> (?&texto_rx))
  39.         (?<expFalsa_rx>     (?&texto_rx))
  40.         (?<texto_rx>  ( [{] (?: [^{}]++ | (?-1) )*+ [}]  ) )
  41.     )
  42. /sx;
  43.  
  44. # Buscamos ifthenelse por todo el documento
  45. #
  46. my @posiciones;
  47.  
  48. while ($archivo =~ /$ifthenelse/g) {
  49.  
  50.     # Cada vez que encontremos un marca ifthenelse, guardamos la posición inicial, final de toda la marca,
  51.     # y el texto de la cláusula verdadera
  52.     push @posiciones, [ $-[1], $+[1], $+{expVerdadero} ];
  53. }
  54.  
  55. # Recorrer en sentido inverso
  56. # Esto es necesario hacerlo así, ya que vamos a hacer cambios en el propio texto.
  57. # Si hiciéramos los cambios desde el principio, las @posiciones ya no serían correctas
  58. # (cada sustitución modifica la longitud de todo el $archivo).
  59. #
  60. for my $pos_ref (reverse @posiciones) {
  61.     # Sustituimos toda la marca ifthenelse, con la expresión verdadera, menos las llaves
  62.     substr($archivo, $pos_ref->[0], $pos_ref->[1] - $pos_ref->[0])
  63.         = substr $pos_ref->[2], 1, -2;
  64. }
  65.  
  66. # Recuperamos las llaves escapadas
  67. #
  68. $archivo =~ s/«/\\{/g;
  69. $archivo =~ s/»/\\}/g;
  70.  
  71. # Resultado
  72. #
  73. say $archivo;
Coloreado en 0.003 segundos, usando GeSHi 1.0.8.4
Sintáxis: [ Descargar ] [ Ocultar ]
Using latex Syntax Highlighting
  1. \documentclass{article}
  2. \newboolean{techrep}
  3. \setboolean{techrep}{false}
  4. \begin{document}
  5. Este es texto
  6. con el que {deseo quedarme} incluidas las llaves \{ que no están
  7. escapadas dentro de el, pero sin las llaves exteriores %
  8. TEXTO TEXTO
  9. TEXTO TEXTO Otro texto
  10. con el que \{deseo quedarme\} incluidas las llaves \{ \}
  11. que no están escapadas
  12. TEXTO TEXTO TEXTO
  13. \end{document}
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4

La expresión de búsqueda recursiva la puedes encontrar en perldoc perlre.
JF^D Perl programming & Raku programming. Grupo en Telegram: https://t.me/Perl_ES
Avatar de Usuario
explorer
Administrador
Administrador
 
Mensajes: 14476
Registrado: 2005-07-24 18:12 @800
Ubicación: Valladolid, España

Re: Modificar texto con expresión regular anidada

Notapor pablgonz » 2019-09-22 15:35 @691

explorer escribiste:Sí, sí que se puede realizar con expresiones regulares, sin problemas.

Bueno... sí que hay un problema que lo complica bastante: la presencia de las llaves escapadas.

Se podría... intentar hacer una exp. reg. que las encuentre, dentro de la exp. reg. general, pero hay una solución más sencilla.

Como de costumbre, tu respuesta es genial y funciona a la perfección, no se me habría ocurrido algo así ni en mis sueños. Ahora que veo tu respuesta me acordé que la expresión regular:
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. my $anidado     = qr/(\{(?>[^\{\}\\]++|\\.|(?R))*+\})    /x;
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4

es una que utilicé en una respuesta que me di yo mismo en Ajustar expresiones regulares en script que al analizarla en profundidad no es correcta.
Si tienes algo de tiempo, ¿podrías darle una mirada a la otra pregunta?
Agradecido por todo.
pablgonz
Perlero nuevo
Perlero nuevo
 
Mensajes: 236
Registrado: 2010-09-08 21:03 @919
Ubicación: Concepción (Chile)


Volver a Básico

¿Quién está conectado?

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