Página 1 de 1

Aplicar autoincremento solo si se cumple una condición

NotaPublicado: 2014-03-05 04:05 @212
por sisifo80
Hola,

Estoy trabajando con archivos .xml y necesitaba hacer una modificación específica. Mi texto de entrada tiene el siguiente formato:

Sintáxis: [ Descargar ] [ Ocultar ]
Using xml Syntax Highlighting
  1.          <p t="opener">
  2.             <w id="23">
  3.               <o>Hola</o>
  4.             </w>
  5.             <w id="24">
  6.               <o>mundo</o>
  7.             </w>
  8.         </p>
Coloreado en 0.000 segundos, usando GeSHi 1.0.8.4

Es decir, cada palabra de un texto está delimitada por la etiqueta <o></o> y además lleva una numeración correlativa que no empieza en 1. Esta numeración aparece como valor del atributo id dentro de la etiqueta <w></w>. Finalmente, hay un nivel superior <p></p> que engloba párrafos enteros.

La salida que necesito tendría el siguiente formato:
Sintáxis: [ Descargar ] [ Ocultar ]
Using xml Syntax Highlighting
  1.          <p t="opener">
  2.             <w id="1">
  3.               <o>Hola</o>
  4.             </w>
  5.             <w id="2">
  6.               <o>mundo</o>
  7.             </w>
  8.         </p>
Coloreado en 0.000 segundos, usando GeSHi 1.0.8.4

Es decir, lo mismo que el anterior pero haciendo que la numeración correlativa comience en 1.

Creo que la solución sería usar el operador de autoincremento (++), pero la cosa se complica, porque necesitaba aplicar esa numeración desde 1 solo si la sección de párrafo lleva el atributo "opener" (como en la entrada anterior). Es decir, lo que no sé hacer en Perl es crear una condición tal que imprima la salida deseada con la numeración desde 1 solo a determinadas secciones de texto (por ejemplo, aquellas que están delimitadas por un <p></p> cuyo atributo es "opener").

Mi propuesta:
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. use warnings;
  2. use strict;
  3. $/ = undef
  4. my $numeración = 0;
  5. my $autoincremento = $numeración++;
  6. my $filename = shift;
  7. open F, $filename or die "Usa: $0 FILENAME\n";
  8. while(<F>) {
  9. if (/<p t=\"opener\".*?<\/p>/s) { #Si encuentras la sección <p t="opener"></p> (el punto (.) equivale a cualquier caracter incluyendo cambio de línea)
  10. s/<w id=\".*?\"/<w id=\"$autoincremento\"/ge #sustituye el valor de id por la variable $autoincremento
  11. }
  12. }
  13. close F;
Coloreado en 0.002 segundos, usando GeSHi 1.0.8.4

Soy consciente de que en realidad no le estoy diciendo a Perl que aplique la sustitución solo en la sección deseada, simplemente le estoy diciendo que aplique esa sustitución a todo el documento si la condición es verdadera.

¿Alguna sugerencia que me permita aplicar la restricción y depurar mi propuesta?

Gracias.

Re: Aplicar autoincremento solo si se cumple una condición

NotaPublicado: 2014-03-05 17:55 @788
por sisifo80
Había un par de errores en mi propuesta anterior. Quedaría así:

Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. use warnings;
  2. use strict;
  3. $/ = undef;
  4. my $numbering = 0;
  5. my $autonumbering = $numbering++;
  6. my $filename = shift;
  7. open F, $filename or die "Usa: $0 FILENAME\n";
  8. while(<F>) {
  9. if (/<p t=\"opener\".*?<\/p>/s) { #If the paragraph is <p t="opener"></p> (the dot (.) stands for every character, including \n)
  10. s/"<w id=\".*?\""/"<w id=\"$autonumbering\""/ge #replace the value of "id" by the variable $autonumbering
  11. }
  12. }
  13. close F;
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4

Re: Aplicar autoincremento solo si se cumple una condición

NotaPublicado: 2014-03-05 19:16 @844
por explorer
Esta es una manera:

Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. #!/usr/bin/perl
  2. use v5.14;
  3. use autodie;
  4.  
  5. my $archivo;
  6. open my $ARCHIVO, 'kk.xml';
  7. {
  8.         local $/;
  9.         $archivo = <$ARCHIVO>;
  10. }
  11. close $ARCHIVO;
  12.  
  13. my $contador = 1;
  14.  
  15. while ($archivo =~ m{<p t="opener">(.+?)</p>}sg) {
  16.         my($pos_inicio, $pos_final, $captura) = ($-[1], $+[1], $1);
  17.  
  18.         $captura =~ s{<w id="\K(\d+)}{$contador++}ge;
  19.  
  20.        substr($archivo, $pos_inicio, $pos_final - $pos_inicio) = $captura;
  21.  
  22.        pos($archivo) = $pos_inicio + length $captura;
  23.  
  24. }
  25.  
  26. print $archivo;
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4

Re: Aplicar autoincremento solo si se cumple una condición

NotaPublicado: 2014-03-06 03:32 @189
por sisifo80
Muchas gracias, explorer.

Re: Aplicar autoincremento solo si se cumple una condición

NotaPublicado: 2014-03-17 05:37 @276
por sisifo80
Disculpa, explorer.

Este script que me has aconsejado me viene muy bien porque me permite hacer modificaciones a porciones de texto concretas. Es decir, "mientras el texto de entrada esté delimitado por... aplíquense las modificaciones...".

Por eso, como le estoy dando bastante uso, me gustaría entenderlo entero. ¿Podrías explicarme brevemente la lógica que está detrás de tu última línea de código? Me refiero a:
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. pos($archivo) = $pos_inicio + length $captura;
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4

¿Está pensada para poder 'salir' del bucle while()?

Gracias.

Re: Aplicar autoincremento solo si se cumple una condición

NotaPublicado: 2014-03-17 06:59 @332
por explorer
Esa línea, no.

Lo que hace es reposicionar el "puntero" del motor de expresiones regulares, para que sepa que la próxima vez que tenga que analizar $archivo, debe hacerlo en esa determinada posición.

El proceso que hace el bucle es el siguiente:
  • se aplica un patrón a la variable $archivo, para buscar una coincidencia (m), de forma repetida (/g), en una cadena de texto que puede contener caracteres de fin de línea (/s)
  • si el motor de exp. reg. encuentra una coincidencia, salvaguardamos la posición inicial, y final de la captura, así como la propia captura del primer par de paréntesis del patrón
  • en la $captura, sustituimos (s) lo que coincida con el patrón -pero solo a partir de la marca '\K'- con la ejecución (/e) de un código Perl (el autoincremento del $contador, y devuelve su valor). Y esto, para toda la $captura (/g)
  • la $captura modificada (o no) es "insertada" en su posición anterior dentro de $archivo, acomodando espacio según haya cambiado su longitud (en más o en menos)
  • como la $captura puede haber cambiado de longitud, no podemos fiarnos de que en la siguiente vuelta el motor de exp. reg. detecte correctamente el siguiente patrón, así que "reposicionamos" el puntero a justo donde acabó la $captura. Así, sabemos que empezará a buscar el siguiente 'opener' a partir de ahí
Ahora que lo veo... sobran los paréntesis de la línea 18 :)

Re: Aplicar autoincremento solo si se cumple una condición

NotaPublicado: 2014-03-17 08:41 @403
por sisifo80
¡Mil gracias! Me ha quedado clarísimo.