• Publicidad

Imprimir texto en un lugar determinado

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

Imprimir texto en un lugar determinado

Notapor sisifo80 » 2014-02-24 09:47 @449

Hola,

Estoy intentando crear un script que modifique ciertas características de un texto escrito en XML. El texto 'input' sería algo así:
Sintáxis: [ Descargar ] [ Ocultar ]
Using xml Syntax Highlighting
  1. <etiqueta1>
  2.    <etiqueta2>text texto texto. PS6000</etiqueta2>
  3. </etiqueta1>
Coloreado en 0.000 segundos, usando GeSHi 1.0.8.4


El texto contenido en <etiqueta2> siempre termina con un código del tipo "PS + cuatro dígitos" (por ejemplo, PS6000).

Lo que necesito es un script que convierta el texto 'input' en el siguiente texto 'output':
Sintáxis: [ Descargar ] [ Ocultar ]
Using xml Syntax Highlighting
  1. <etiqueta1 n="PS6000">
  2.    <etiqueta2>text texto texto. PS6000</etiqueta2>
  3. </etiqueta1>
Coloreado en 0.000 segundos, usando GeSHi 1.0.8.4

Es decir, un script que reconozca el código que aparece al final de <etiqueta2> y lo repita como valor del atributo "n" dentro de <etiqueta1>.

Concretamente, lo que no sé es cómo decir en Perl que si encuentra determinada cadena la imprima en un lugar determinado. Mi propuesta:
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. use warnings;
  2. use strict;
  3. my $filename = shift;
  4. open F, $filename or die "Usa: $0 FILENAME\n";
  5. while (<F>) {
  6.     if (/(PS\d\d\d\d)<\/etiqueta2>/) {
  7.         print $1;
  8.     }                                  # si encuentra "PS + cuatro dígitos + </etiqueta2>" imprima "PS + cuatro dígitos"
  9. }
  10. close F;
Coloreado en 0.003 segundos, usando GeSHi 1.0.8.4
sisifo80
Perlero nuevo
Perlero nuevo
 
Mensajes: 32
Registrado: 2013-11-20 07:30 @354

Publicidad

Re: Imprimir texto en un lugar determinado

Notapor explorer » 2014-02-24 11:38 @526

Cuando se trata de modificar una línea que está antes de la que estamos leyendo, no nos vale la opción de ir leyendo el archivo línea a línea, ya que al leer la segunda línea, olvidamos la primera.

Una opción es leer todo el archivo en memoria, y aplicar una expresión regular, ya que, el cambio que quieres hacer se puede expresar fácil como un patrón:
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. #!/usr/bin/perl
  2. use File::Slurp;
  3.  
  4. my $xml = read_file('code_36827.xml');                                      # leemos todo el archivo
  5.  
  6. $xml =~ s{<etiqueta1\K(>\s+<etiqueta2>.+(PS\d+)</etiqueta2>)}{ n="$2"$1}g;  # hacemos cambios
  7.  
  8. print $xml;                                                                 # resultado
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4

La expresión regular quiere decir:
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. $xml =~ s{                      # operación de sustitución
  2.     <etiqueta1                  # buscamos esto
  3.     \K                          # si lo encontramos, nos olvidamos de él, y miramos resto del patrón
  4.     (                           # capturar en $1 lo que sigue
  5.         >                       # buscamos un '>'
  6.         \s+                     # seguido por uno o más caracteres de espacio (fines de línea incluidos)
  7.         <etiqueta2>             # seguido por esta etiqueta
  8.         .+                      # seguido por uno o más caracteres cualesquiera
  9.         (                       # capturar en $2 lo que sigue
  10.             PS                  # seguido por 'PS'
  11.             \d+                 # seguido por un conjunto de dígitos
  12.         )                       # fin de captura $2: es el código que queremos poner
  13.         </etiqueta2>            # seguido por la etiqueta de cierre
  14.     )
  15. }
  16. { n="$2"$1}gx;                  # si hemos encontrado un texto que coincida con lo anterior, lo cambiamos por esto
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4

Otra forma de hacerlo es usando las facilidades de Tie::File, que nos relaciona las líneas del archivo con un array, por lo que ya podemos usar el concepto "línea anterior":
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. use Tie::File;
  2.  
  3. tie my @xml, 'Tie::File', 'code_36827.xml';                     # enlazamos el archivo con el array @xml
  4.  
  5. for(my $i = 0; $i < @xml; $i++) {                               # para todas las líneas
  6.  
  7.     if ($xml[$i] =~ m{ (PS\d+)</etiqueta2>}) {                  # si la línea tiene un código
  8.  
  9.         my $codigo = $1;
  10.  
  11.         $xml[$i-1] =~ s{<etiqueta1>}{<etiqueta1 n="$codigo">};  # hacemos el cambio en la línea anterior
  12.     }
  13. }
  14.  
  15. untie @xml;                                                     # final de enlazado (al archivo queda modificado)
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: 14476
Registrado: 2005-07-24 18:12 @800
Ubicación: Valladolid, España

Re: Imprimir texto en un lugar determinado

Notapor sisifo80 » 2014-02-24 11:55 @538

¡Muchas gracias, explorer!

Antes de leer tu respuesta, yo había intentado una solución mucho más "chapucera":

Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. use warnings;
  2. use strict;
  3. my $filename = shift;
  4. open F, $filename or die "Usa: $0 FILENAME\n";
  5. while (<F>) {
  6. if (/(PS\d\d\d\d)<\/etiqueta2>/) {print "<etiqueta1 n=\"$1\">\n";next}
  7. }
  8. close F;
  9. open F, $filename or die "Usa: $0 FILENAME\n";
  10. while (<F>) {
  11. if (/<etiqueta2>/) {print;next}
  12. }
  13. close F;
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4


Es decir, abrir el archivo, encontrar el patrón e imprimirlo dentro de "etiqueta1", cerrar el archivo, abrir de nuevo, imprimir "etiqueta2.

Pero, obviamente, tus sugerencias son mucho mejores. ¡Gracias de nuevo!
sisifo80
Perlero nuevo
Perlero nuevo
 
Mensajes: 32
Registrado: 2013-11-20 07:30 @354


Volver a Básico

¿Quién está conectado?

Usuarios navegando por este Foro: Bing [Bot] y 10 invitados