• Publicidad

Parsear un fichero con formato SMS

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

Parsear un fichero con formato SMS

Notapor Santi » 2007-05-28 08:32 @397

Buenas,

Necesito parsear un fichero de texto con formato SMS, la sintaxis es la utilizada por las "smstools", ejemplos en la página:

http://smstools.meinemullemaus.de/fileformat.html

Un ejemplo de un SMS sería:

From: 2004
From_SMSC: 34607003110
Sent: 07-05-28 14:37:30
Received: 07-05-28 14:37:39
Subject: GSM1
Alphabet: ISO
UDH: false

server-xx webrestart


Por lo que he visto en la sintaxis la forma de parsear el fichero podría ser:

1) Hasta encontrar una línea en blanco procesar los tokens.
2) Una vez encontrada la línea en blanco empieza el mensaje de texto.

La primera parte de procesar los tokens ya la tengo lista-.. me faltaría la parte de detectar la línea en blanco y dejar de procesar los tokens para empezar a almacenar el SMS en otra variable.

El código para extraer las variables sería:

Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
open( FILE, "< GSM1.PWVZXW " ) or die "ERROR: $!";
while (<FILE>) {
                # Ignoramos las líneas que son comentarios
                next if /^\s*#/;
                # Eliminar el caracter \n del final
                chomp;
                # Dividir la línea en variable y valor
                ($var, $value) = split(":", $_);
                # Pasamos a mayúsculas el nombre de la variable
                $var =~ tr/[a-z]/[A-Z]/;
                # Si el valor empieza y acaba por comillas dobles las eliminamos
                $value =~ s/^\"(.+)\"$/$1/;
                # Metemos los datos en un array asociativo
                $conf{$var} = $value;
}
close(FILE);
Coloreado en 0.003 segundos, usando GeSHi 1.0.8.4


¿Creéis que es correcta la forma de procesar el fichero? ¿Alguna pista para detectar la línea en blanco y dejar de procesar los tokens? Me interesa que el resto, quede guardado dentro de la variable "$conf{'SMS'}".

Si conocéis algún módulo para procesar este tipo de ficheros.. también es bienvenido :-)

Saludos,
Santi Saez
Santi
Perlero nuevo
Perlero nuevo
 
Mensajes: 13
Registrado: 2006-04-07 08:56 @414

Publicidad

Notapor monoswim » 2007-05-28 10:33 @481

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 explorer » 2007-05-28 17:13 @759

A mí me parece correcta la forma de procesar el fichero. Podrías salir del bucle y entrar en el bucle de leer el mensaje en cuanto detectes la línea en blanco. Para agregar todo el mensaje en una variable puedes usar el operador de concatenación de strings (.). La línea podría quedar así: $conf{SMS} .= $linea;. Para detectar la línea en blanco podrías usar una expresión regular como /^$/ o simplemente, un if con un eq a un ''.

Me suena que puede haber algún módulo que procese este tipo de ficheros, ya que se parece mucho, por no decir que es casi idéntico, al de un correo electrónico.

La siguiente solución es un pelín oscura, así que la explico más abajo.

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

use Data::Dumper;

while ( <DATA> ) {
    chomp;

    if ( 1 .. /^$/ ) {

        next if /^$/;

        map { $conf{ uc $_->[0] } = $_->[1] } [ split / *: */, $_, 2 ];

    }
    else {
        $conf{ SMS } .= "$_ ";
    }

}

chop $conf{ SMS };

print Dumper(\%conf);

__DATA__
From: 2004
From_SMSC: 34607003110
Sent: 07-05-28 14:37:30
Received: 07-05-28 14:37:39
Subject: GSM1
Alphabet: ISO
UDH: false

server-xx webrestart
alioli restarting
asp pages down
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4
Salida:
Código: Seleccionar todo
$VAR1 = {
          'SMS' => 'server-xx webrestart alioli restarting asp pages down',
          'SUBJECT' => 'GSM1',
          'UDH' => 'false',
          'RECEIVED' => '07-05-28 14:37:39',
          'FROM_SMSC' => '34607003110',
          'SENT' => '07-05-28 14:37:30',
          'FROM' => '2004',
          'ALPHABET' => 'ISO'
        };

* El bucle de lectura, en este ejemplo, en lugar de hacerlo sobre un fichero, lo hacemos sobre la parte DATA, pero es sólo para abreviar el ejemplo. Usamos un while para leer de __DATA__ mientras nos dé líneas.
* Inmediatamente, quitamos el final de línea con chomp.
* Y ahora viene una cosa rara: es el operador binario rango '..'. Lo normal es verlo en contexto lista, donde nos devuelve el rango de números ( for (1 .. 100) {}), pero en esta ocasión está en contexto escalar. En este modo, se comporta de forma diferente: como un valor booleano. El operador es como un biestable, según se cumplan alguna de las dos condiciones separadas por los puntos. En este caso, la primera expresión se compara con el valor de la variable '$.', que contiene el número de línea del fichero leído (el '1' es lo mismo que si hubiéramos escrito '$. == 1'), así que estamos mirando por si hemos leído la primera línea del fichero de donde estamos leyendo. Y la expresión regular final indica que será cierta cuando lleguemos a la famosa línea en blanco.
El resultado de todo esto es que el primer if se ejecutará mientras a) pasemos de la primera línea y b) no hemos llegado a la línea en blanco. De hecho, también procesará la línea en blanco. Por eso ponemos un next para que pase a la siguiente (no vamos a hacer nada con ella).
* La línea siguiente es un poco más enrevesada... con split dividimos la línea por los dos puntos, aprovechando para quitar los espacios en blanco. Ponemos además un '2' para indicar que vamos a cortar por el primer carácter ':' y sólo por ese.
El resultado es una lista de dos valores. Los metemos en un array anónimo ([]) y se lo pasamos a un map.
El map coge el array anónimo que le pasamos por $_ y le extrae su primera componente ($_->[0]), la pasa a mayúsculas con la función uc() y se la da como clave a %conf, mientras que como valor le damos la segunda componente del array ($_->[1]).
* En caso de que no se cumpla el if quiere decir que estamos en la parte del mensaje. Ahí sólo tenemos que ir agregando cada línea a $conf{ SMS }. Aquí tenemos el problema de que si hemos quitado los retorno de carro antes, al juntar las líneas saldrán pegados el último carácter de una línea con el primer carácter de la siguiente. Por eso le añadimos un blanco al final.
* Al final del bucle quitamos el último carácter blanco que sobra en el mensaje construido.
* Con la ayuda del módulo Data::Dumper vemos cómo ha quedado la variable %conf.

No está puesto lo de quitar las comillas dobles, pero eso ya veo que lo sabes hacer.

Seguro que hay más formas de hacerlo, claro.

En el enlace que te pongo, en la sección Range Operators, hay más ejemplos de cómo leer ficheros compuestos de cabecera y cuerpo, del estilo de tus sms.
JF^D Perl programming & Raku programming. Grupo en Telegram: https://t.me/Perl_ES
Avatar de Usuario
explorer
Administrador
Administrador
 
Mensajes: 14482
Registrado: 2005-07-24 18:12 @800
Ubicación: Valladolid, España

Notapor Santi » 2007-05-31 07:21 @348

Hola,

En primer agradecer las respuestas de monoswim y especialmente la de explorer, pedazo de respuesta elaborada, ¡así da gusto! ¡Muchísimas gracias! :-)

Monoswim: antes de ponerme con ello estuve buscando algún módulo, pero no he encontrado nada que me pueda ayuda.

Para explorer: al final he optado por una solución que me es más sencillo de entender y por tanto mantener, la copio por si queréis hacer algún comentario:


Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
sub ParseSMS {
        my $sms = shift;
        my $var;
        my $value;
        my %conf;
        open( FILE, "< $sms " ) or die "ERROR al abrir el fichero SMS: $!";
        while (<FILE>) {
                # Eliminar el carácter \n del final
                chomp;
                # Dividir la linea en variable y valor
                ($var, $value) = split(":", $_);
                # Pasamos a mayúsculas el nombre de la variable
                $var =~ tr/[a-z]/[A-Z]/;
                # Si el valor empieza y acaba por comillas dobles las eliminamos
                $value =~ s/^\"(.+)\"$/$1/;
                # Metemos los datos en un array asociativo
                $conf{$var} = $value;
                # Si nos encontramos con una línea en blanco dejar de procesar los tokens
                last if /^$/;
        }

        # Tras los tokens y la línea en blanco llega el contenido del SMS
        $conf{'SMS'} = join("", <FILE> );

        # Eliminar el carácter \n del final
        chomp $conf{'SMS'};

        # Cerrar el SMS y devolver el array asociativo
        close(FILE);
        return %conf;
}
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4


Con esto consigo lo que buscaba.. muchas gracias a los dos otra vez más :-)

Saludos,
Santi Saez
Santi
Perlero nuevo
Perlero nuevo
 
Mensajes: 13
Registrado: 2006-04-07 08:56 @414

Notapor explorer » 2007-05-31 08:58 @415

Yo pondría el 'last' como primera sentencia a ejecutar dentro del 'while'.
JF^D Perl programming & Raku programming. Grupo en Telegram: https://t.me/Perl_ES
Avatar de Usuario
explorer
Administrador
Administrador
 
Mensajes: 14482
Registrado: 2005-07-24 18:12 @800
Ubicación: Valladolid, España

Notapor Santi » 2007-06-01 05:40 @277

Hola,

Gracias por el tip, cambiado el if al principio del while :-)

Saludos,
Santi Saez
Santi
Perlero nuevo
Perlero nuevo
 
Mensajes: 13
Registrado: 2006-04-07 08:56 @414


Volver a Básico

¿Quién está conectado?

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