• Publicidad

Parseo de XML muy raro

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

Parseo de XML muy raro

Notapor Sombrerero_Loco » 2008-12-19 06:12 @300

¡Buenas!

Vuelvo a la carga con mis preguntas.

Al turrón: tengo que parsear un documento XML que no cumple ningún estándar de XML, tiene la extensión de xml pues porque tenían que ponerle una. Ciertamente se parece mucho a xml y usa las mismas jerarquías de estructuras, pero ni cumple DTD ni Schema. Esto descarta poder utilizar módulos de parseo de Perl, con lo que debo de hacerlo con expresiones regulares.

Vale, el fichero "xml" es algo tal que así (tiene aprox. 10.000 líneas de ese estilo):

Sintáxis: [ Descargar ] [ Ocultar ]
Using xml Syntax Highlighting
<Service>
<Name>opc_adm (discovered)</Name>
<Label>opc_adm</Label>
<MsgWeight>1.0</MsgWeight>
<MsgPropRuleRef>Unchanged</MsgPropRuleRef>
<Icon>services.32.gif</Icon>
<CalcRuleRef>Most critical</CalcRuleRef>
<Source>
<Dependency/>
<ServiceRef>SVCDISC:System</ServiceRef>
<PropRuleRef>Unchanged</PropRuleRef>
<Weight>1.0</Weight>
</Source>
<Source>
<Dependency/>
<ServiceRef>SVCDISC:Applications</ServiceRef>
<PropRuleRef>Unchanged</PropRuleRef>
<Weight>1.0</Weight>
</Source>
<Source>
<Dependency/>
<ServiceRef>HP OpenView Operations</ServiceRef>
<PropRuleRef>Unchanged</PropRuleRef>
<Weight>1.0</Weight>
</Source>
<Source>
<Dependency/>
<ServiceRef>SGC</ServiceRef>
<PropRuleRef>Unchanged</PropRuleRef>
<Weight>1.0</Weight>
</Source>
</Service>
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4


Esto se lo enchufas a otra aplicación que se traga ese fichero y crea un árbol de jerarquías, etc. Bien, hasta ahí todo correcto.

He hecho este script que lo que hace es leerme el fichero, contar hasta 10000 e irme imprimiendo "en teoría" cada línea que lee del fichero xml, para ver, sencillamente que Perl lo lee y lo escribe como yo quiero. Este es el script:

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

use warnings;
use diagnostics


my $carpeta = "C:\\XMLparser\\";
my $fichero = "xml_mutilado_pruebas.xml";
my $rutacompleta =  $carpeta . $fichero ;
my $dumpfile = "volcado.txt";
my $dumpall = $carpeta . $dumpfile;
my $contador = 0;

if (!(-e $dumpall)) {
    open (DUMPFILEOUT, ">>$dumpall") or die "No se ha podido actualizar el fichero de Log (log_file.txt)\n";
    close DUMPFILEOUT;
}

open (PARSEO, "<$rutacompleta");
open (DUMPFILEOUT, ">>$dumpall");
foreach $lector(<PARSEO>) {
    while ($contador < 10000) {
        printf DUMPFILEOUT "$lector ::: numero de iteracion: $contador\n";
        $contador++;
        #print "$contador\n";
    }
}

close PARSEO;
close DUMPFILEOUT;
Coloreado en 0.002 segundos, usando GeSHi 1.0.8.4


Lo curioso es que me hace las 10.000 iteraciones y me abre el fichero que le indico que me escriba, y me escribe siempre en cada línea esto:
Código: Seleccionar todo
<Service>
 ::: numero de iteracion: 0
<Service>
 ::: numero de iteracion: 1
<Service>
 ::: numero de iteracion: 2
<Service>
 ::: numero de iteracion: 3
<Service>
 ::: numero de iteracion: 4
<Service>
 ::: numero de iteracion: 5
<Service>
 ::: numero de iteracion: 6
<Service>
 ::: numero de iteracion: 7
<Service>
 ::: numero de iteracion: 8
<Service>
 ::: numero de iteracion: 9
<Service>
 ::: numero de iteracion: 10
<Service>
 ::: numero de iteracion: 11
<Service>
 ::: numero de iteracion: 12


Así hasta la 10.000, sinceramente no se qué está haciendo mal, porque el fichero de origen es el que os he puesto y aquí me da la impresión de que está constantemente leyendo la primera línea y contando 1, así hasta 10.000.

Como leí recientemente el bug es uno, no Perl, así que entiendo que estoy diciéndole algo mal, o simplemente no le estoy diciendo algo a Perl.

¿Alguna idea de que me puede estar fallando y/o faltando?

¡¡Un saludo y aprovecho para felicitaros la navidad!!
Sombrerero_Loco
Perlero nuevo
Perlero nuevo
 
Mensajes: 42
Registrado: 2008-11-11 11:20 @514

Publicidad

Notapor monoswim » 2008-12-19 07:15 @344

Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
foreach $lector(<PARSEO>) {
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4


Eso está mal, debes hacer

Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
while (my $lector = <PARSEO>){
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4


Saludos
MonoSwim
Perl Programming Language
Avatar de Usuario
monoswim
Perlero nuevo
Perlero nuevo
 
Mensajes: 452
Registrado: 2003-11-18 16:13 @717
Ubicación: Buenos Aires

Notapor Sombrerero_Loco » 2008-12-19 07:30 @354

Nada, se me queda en lo mismo. No pasa de la primera línea.
Sombrerero_Loco
Perlero nuevo
Perlero nuevo
 
Mensajes: 42
Registrado: 2008-11-11 11:20 @514

Notapor explorer » 2008-12-19 18:06 @796

Analicemos con detalle lo escrito por Sombrerero_Loco:
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
foreach $lector(<PARSEO>) {
    while ($contador < 10000) {
        printf DUMPFILEOUT "$lector ::: numero de iteracion: $contador\n";
        $contador++;
        #print "$contador\n";
    }
}
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4

Desmenuzado:
Código: Seleccionar todo
Por cada línea del fichero
  Comienza un bucle hasta que el $contador llega a 10.000
    Imprime la salida
    Se incrementa el $contador

Es decir, el resultado es (aún mas desmenuzado):
Código: Seleccionar todo
Leemos la primera línea del fichero
Se imprime esa línea 10.000 veces (y $contador termina valiendo 10.000, claro)
Leemos la segunda línea del fichero
y no hacemos nada con ella (porque $contador vale 10.000 => no entra en el bucle while)
Leemos la tercera línea
y no hacemos nada con ella (porque $contador vale 10.000 => no entra en el bucle while)
Leemos la cuarta línea
y no hacemos nada con ella (porque $contador vale 10.000 => no entra en el bucle while)
...
hasta fin de fichero


Así que hay un problema: tenemos dos bucles que no funcionan bien.

Además, estás usando printf(), no print(). La diferencia es que printf() NECESITA una cadena de formato.

Está claro cómo hacer un bucle para sacar todo el fichero. La cuestión podría ser cómo sacar las primeras 10.000 líneas. Esta es una posible solución:
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
for my $contador (1 .. 10_000) {
    my $lector = <PARSEO>;
    print DUMPFILEOUT "$lector ::: número de iteración: $contador\n";
}
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4
Esta es otra
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
my $contador = 1;
while ( $contador <= 10_000 ) {
    my $lector = <PARSEO>;
    print DUMPFILEOUT "$lector ::: número de iteración: $contador\n";
    $contador++;
}
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4

O mejor:
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
my $contador = 1;
while ( $contador <= 10_000 and my $lector = <PARSEO> ) {
    print DUMPFILEOUT "$lector ::: número de iteración: $contador\n";
    $contador++;
}
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4
Así, leemos 10.000 líneas o menos, si el fichero no tiene tantas.

P.D. Lo normal es poner un bucle como el que indica monoswim (con el while()), pero también se puede hacer con un for() o foreach(). El resultado es el mismo (un bucle por todas las líneas), pero la forma de hacerlo es "ligeramente" distinta. Mientras que el bucle while() va leyendo una línea cada vez, en el bucle foreach() Perl se lee TODO el fichero (todas las líneas), y las va recorriendo una a una.
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

Re: Parseo de XML muy raro

Notapor explorer » 2008-12-19 18:50 @827

Sombrerero_Loco escribiste:Ciertamente se parece mucho a xml y usa las mismas jerarquías de estructuras, pero ni cumple DTD ni Schema. Esto descarta poder utilizar módulos de parseo de Perl, con lo que debo de hacerlo con expresiones regulares.

Bueno, pues si hay que descartar a los módulos de parseo, utilizaremos algo más sencillo...
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
#!/usr/bin/perl
use XML::Simple;
use Data::Dumper;

my $config = XMLin();

print Dumper $config;
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4
Sale:
Código: Seleccionar todo
$VAR1 = {
          'MsgWeight' => '1.0',
          'Icon' => 'services.32.gif',
          'CalcRuleRef' => 'Most critical',
          'MsgPropRuleRef' => 'Unchanged',
          'Source' => [
                      {
                        'ServiceRef' => 'SVCDISC:System',
                        'Dependency' => {},
                        'Weight' => '1.0',
                        'PropRuleRef' => 'Unchanged'
                      },
                      {
                        'ServiceRef' => 'SVCDISC:Applications',
                        'Dependency' => {},
                        'Weight' => '1.0',
                        'PropRuleRef' => 'Unchanged'
                      },
                      {
                        'ServiceRef' => 'HP OpenView Operations',
                        'Dependency' => {},
                        'Weight' => '1.0',
                        'PropRuleRef' => 'Unchanged'
                      },
                      {
                        'ServiceRef' => 'SGC',
                        'Dependency' => {},
                        'Weight' => '1.0',
                        'PropRuleRef' => 'Unchanged'
                      }
                    ],
          'Label' => 'opc_adm',
          'Name' => 'opc_adm (discovered)'
        };
Quizás te vale así...

Fíjate que en el programa no he dicho qué fichero había que leer. He usado un truco de XML::Simple: si no se indica el fichero, el módulo busca uno que se llame igual que el propio programa, pero acabado en '.xml'.
Última edición por explorer el 2008-12-22 11:39 @527, editado 1 vez en total
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 Jenda » 2008-12-19 19:58 @873

Y como autor de uno de los parsers no puedo dejarlo sin anunciar :-)


Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
use strict;
use XML::Rules;
use Data::Dumper;

my $parser = XML::Rules->new(
        stripspaces => 7,
        rules => {
                _default => 'content', # de la mayoría del tags queremos el contenido
                Source => sub {return '%Sources' => [delete($_[1]->{ServiceRef}) => $_[1]]},
                        # queremos añadir los datos a la hash Sources usando el contenido del <ServiceRef> como la llave
                Service => 'pass',
        }
);

my $data = $parser->parse(\*DATA);
print Dumper($data);

__DATA__
<Service>
<Name>opc_adm (discovered)</Name>
<Label>opc_adm</Label>
<MsgWeight>1.0</MsgWeight>
<MsgPropRuleRef>Unchanged</MsgPropRuleRef>
<Icon>services.32.gif</Icon>
<CalcRuleRef>Most critical</CalcRuleRef>
<Source>
<Dependency/>
<ServiceRef>SVCDISC:System</ServiceRef>
<PropRuleRef>Unchanged</PropRuleRef>
<Weight>1.0</Weight>
</Source>
<Source>
<Dependency/>
<ServiceRef>SVCDISC:Applications</ServiceRef>
<PropRuleRef>Unchanged</PropRuleRef>
<Weight>1.0</Weight>
</Source>
<Source>
<Dependency/>
<ServiceRef>HP OpenView Operations</ServiceRef>
<PropRuleRef>Unchanged</PropRuleRef>
<Weight>1.0</Weight>
</Source>
<Source>
<Dependency/>
<ServiceRef>SGC</ServiceRef>
<PropRuleRef>Unchanged</PropRuleRef>
<Weight>1.0</Weight>
</Source>
</Service>
 
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4


¿Cómo le parece?

Espero que no se vea un poco extraño al principio. Este módulo le deja especificar qué datos recordar de cada tag y cómo y cada "rule" obtiene todos los datos del tag (el nombre y los atributos) y los datos recordados por los rules de los subtags.

Jenda
-------------------------------------------------------
- Estoy aquí para practicar español. Si te ayudó mi respuesta ayudame con un mensaje privado sobre mis faltas por favor. Seguramente habrá muchas :-)
Jenda
Perlero nuevo
Perlero nuevo
 
Mensajes: 132
Registrado: 2007-10-29 06:31 @313
Ubicación: Praga, Republica Checa

Notapor Sombrerero_Loco » 2008-12-22 09:19 @430

¡wow! vaya torrente de post :)
Tenéis razón, el problema era el maldito bucle que no estaba bien referenciado, pero hay una cosa que me está dando qué pensar.
Si ejecuto este código que me indicas

Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
#!/usr/bin/perl
use XML::Simple;
use Data::Dumper;

my $config = XMLin();

print Dumper $config;
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4



La consola de cmd me devuelve esto:

Código: Seleccionar todo
Could not find test.xml in C:\XMLPAR~1\ at C:\Trabajo\Proyectos\Desarrollos propios>


Tanto el script como el fichero están en la carpeta C:\XMLparser, el fichero se llama test.pl y de donde debe de sacarlo, test.xml. La verdad, estoy mas que desesperado con esto...
Sombrerero_Loco
Perlero nuevo
Perlero nuevo
 
Mensajes: 42
Registrado: 2008-11-11 11:20 @514

Notapor explorer » 2008-12-22 11:10 @507

Pues nada, cambia el XMLin() para que le digas dónde está el fichero: XMLin('C:/XMLparser/test.xml');

Resulta que una cosa es dónde están los programas y los datos, pero otra cosa es el lugar de trabajo en donde estamos lanzando los programas.
Última edición por explorer el 2008-12-22 11:43 @530, editado 1 vez en total
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 Sombrerero_Loco » 2008-12-22 11:40 @528

¡Gracias!
Aunque no era eso, resulta que me daba un error de "junk data" y era porque el xml no estaba bien declarado.
Cada vez que quiera cargar ese fichero, como pediré una copia cada vez que lo cargue el memoria, voy a tener que retocarlo antes de lanzarlo en el Perl, pero bueno.

Ahora que lo tengo cargado, tengo otra traba.

Si os dais cuenta es un árbol de XML, pero declarado secuencialmente, no en forma de tree.

Me explico, lo que está en rojo es el meollo del XML. Abre SERVICE, declara elementos del tree y para el elemento del tree que está en rojo que es opc_adm, no abre otro tag y lo declara como un hijo del padre, sino que cierra el SERVICE, abre otro SERVICE y ahí lo declara.

Como veis esto no cumple XML ni por asomo. He estado googleando a un chico que le pasaba lo mismo, terminó usando el XML::Twig, aunque yo no le he encontrado la utilidad a esto.

Como veis soy más un cenutrio que nada, y la verdad que hoy me siento bastante atorado.

¿Cómo se os ocurre que se puede hacer esto? ¿con expresiones regulares? (como en un principio pensé) o preformateando el xml (cosa que no puedo, porque no se puede configurar la salida)...

¡Gracias!


<Service>
<Name>69970093-C777-494C-BE61-E9CDBFF99582</Name>
<Label>Virtual top node (don't remove)</Label>
<Description>Dont delete</Description>
<Source>
<Composition/>
<ServiceRef>opc_adm (discovered)</ServiceRef>
</Source>

<Source>
<Composition/>
<ServiceRef>usuario1 (discovered)</ServiceRef>
</Source>
<Source>
<Composition/>
<ServiceRef>usuario2(discovered)</ServiceRef>
</Source>
<Source>
<Composition/>
<ServiceRef>usuario3 (discovered)</ServiceRef>
</Source>
<Source>
<Composition/>
<ServiceRef>usuario4 (discovered)</ServiceRef>
</Source>
<Source>
<Composition/>
<ServiceRef>usuario5 (discovered)</ServiceRef>
</Source>
</Service>
<Service>
<Service>
<Name>opc_adm (discovered)</Name>
<Label>opc_adm</Label>
<MsgWeight>1.0</MsgWeight>
<MsgPropRuleRef>Unchanged</MsgPropRuleRef>
<Icon>services.32.gif</Icon>
<CalcRuleRef>Most critical</CalcRuleRef>
<Source>
<Dependency/>
<ServiceRef>SVCDISC:System</ServiceRef>
<PropRuleRef>Unchanged</PropRuleRef>
<Weight>1.0</Weight>
</Source>
<Source>
<Dependency/>
<ServiceRef>SVCDISC:Applications</ServiceRef>
<PropRuleRef>Unchanged</PropRuleRef>
<Weight>1.0</Weight>
</Source>
<Source>
<Dependency/>
<ServiceRef>HP OpenView Operations</ServiceRef>
<PropRuleRef>Unchanged</PropRuleRef>
<Weight>1.0</Weight>
</Source>
<Source>
<Dependency/>
<ServiceRef>SGC</ServiceRef>
<PropRuleRef>Unchanged</PropRuleRef>
<Weight>1.0</Weight>
</Source>
</Service>
Sombrerero_Loco
Perlero nuevo
Perlero nuevo
 
Mensajes: 42
Registrado: 2008-11-11 11:20 @514

Notapor explorer » 2008-12-22 12:32 @563

Pues sí que es raro... veo dos veces que se abre <Services>, seguidas... ¿no será un error del copia y pega?

Si no te llega el fichero entero, quizás sea mejor usar expresiones regulares (todavía no queda claro qué es lo que quieres extraer). Pero muy posiblemente con el módulo de Jenda se pueda hacer.

El caso es que no importa dónde esté la información si sabemos localizarla y siempre aparece de la misma forma. Podríamos admitir que la información de 'opc_adm' está demasiado repartida, si de todas formas podemos leerla.

Pero en tu caso, ya te puedo decir que XML::Simple no te sirve: el módulo no admite que existan varios nodos raíz. O lo parcheas (poniendo un nodo raíz a todos) o usas otro sistema.

Ejemplo. Supongamos que
1.- El tag <Services> que me parece que está demás, desaparece
2.- Rodeamos a todo el contenido con los tag <root> (por ejemplo)

Entonces, con el programa siguiente:
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
#!/usr/bin/perl
use strict;
use warnings;

use XML::Simple;
use Data::Dumper;

my $config = XMLin('kk.xml', ForceArray => ['Source']);

print Dumper $config;

## Comprobar que el primer nodo es el 'top'
die "ERROR en el primero nodo: no es top\n"
    if $config->{Service}[0]{Label} !~ /top node/;


## Sacar la lista de ServiceRef encontrados
for my $serviceref ( @{ $config->{Service}[0]{Source} } ) {
    print $serviceref->{ServiceRef}, "\n";
}

print "\n";

## Sacar los Source de opc_adm
for my $service ( @{ $config->{Service} } ) {
    if ($service->{Name} eq 'opc_adm (discovered)') {
        for my $source ( @{ $service->{Source} } ) {
            print $source->{ServiceRef}, "\n";
        }
    }
}
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4
Sale:
Código: Seleccionar todo
$VAR1 = {
          'Service' => [
                         {
                           'Source' => [
                                         {
                                           'ServiceRef' => 'opc_adm (discovered)',
                                           'Composition' => {}
                                         },
                                         {
                                           'ServiceRef' => 'usuario1 (discovered)',
                                           'Composition' => {}
                                         },
                                         {
                                           'ServiceRef' => 'usuario2(discovered)',
                                           'Composition' => {}
                                         },
                                         {
                                           'ServiceRef' => 'usuario3 (discovered)',
                                           'Composition' => {}
                                         },
                                         {
                                           'ServiceRef' => 'usuario4 (discovered)',
                                           'Composition' => {}
                                         },
                                         {
                                           'ServiceRef' => 'usuario5 (discovered)',
                                           'Composition' => {}
                                         }
                                       ],
                           'Description' => 'Dont delete',
                           'Label' => 'Virtual top node (don\'t remove)',
                           'Name' => '69970093-C777-494C-BE61-E9CDBFF99582'
                         },
                         {
                           'MsgWeight' => '1.0',
                           'Icon' => 'services.32.gif',
                           'CalcRuleRef' => 'Most critical',
                           'MsgPropRuleRef' => 'Unchanged',
                           'Source' => [
                                         {
                                           'ServiceRef' => 'SVCDISC:System',
                                           'Dependency' => {},
                                           'Weight' => '1.0',
                                           'PropRuleRef' => 'Unchanged'
                                         },
                                         {
                                           'ServiceRef' => 'SVCDISC:Applications',
                                           'Dependency' => {},
                                           'Weight' => '1.0',
                                           'PropRuleRef' => 'Unchanged'
                                         },
                                         {
                                           'ServiceRef' => 'HP OpenView Operations',
                                           'Dependency' => {},
                                           'Weight' => '1.0',
                                           'PropRuleRef' => 'Unchanged'
                                         },
                                         {
                                           'ServiceRef' => 'SGC',
                                           'Dependency' => {},
                                           'Weight' => '1.0',
                                           'PropRuleRef' => 'Unchanged'
                                         }
                                       ],
                           'Label' => 'opc_adm',
                           'Name' => 'opc_adm (discovered)'
                         }
                       ]
        };
opc_adm (discovered)
usuario1 (discovered)
usuario2(discovered)
usuario3 (discovered)
usuario4 (discovered)
usuario5 (discovered)

SVCDISC:System
SVCDISC:Applications
HP OpenView Operations
SGC
Ahora bien... recordar que XML::Simple es eso: simple, así que el módulo no nos deja las cosas muy sencillas...

Es MUCHÍSIMO mejor usar otro módulo de parseo de XML.
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 Básico

¿Quién está conectado?

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

cron