• Publicidad

Buscar en un archivo

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

Buscar en un archivo

Notapor xchidalgox » 2007-04-08 14:24 @641

Hola

Estoy realizando una búsqueda en un archivo a partir de otro, y hago un merge de los archivos
Ejemplo:

input1.txt
Código: Seleccionar todo
'011200190ABELLÁN ABELLÁN  CELIA     '
'011200191ABELLÁN LLOBREGATSANDRA    '
'011200192AGUILAR ESCRIBANOJAVIER    '
'011200193AGULLÓ CRESPO    ANA ISABEL'
'011200194ALBERO GARCÍA    ELENA     '
'011200195ALBERT SÁNCHEZ   CRISTINA  '
'011200196ALCARAZ BUADES   DAVID     '
'011200197ALDEGUER QUESADA SANTIAGO  '
'011200198ALDEGUER SERRANO REBECA    '

El archivo es posicional y ésta es su estructura:
Desde la posición 0 a la posición 1 es el Identificador del archivo.
Desde la posición 2 a la posición 8 es el Identificador de usuario.
Desde la posición 9 a la posición 35 es el Nombre usuario.

input2.txt
Código: Seleccionar todo
'021200190097876541'
'021200191087876544'
'021200192089693803'
'021200194098765432'
'021200195098765232'
'021200196089768124'
'021200198097521358'

El archivo es posicional, esta es la estructura:
Desde la posición 0 a la posición 1 es el Identificador de archivo.
Desde la posición 2 a la posición 8 es el Identificador de usuario.
Desde la posición 9 a la posición 17 es el Numero de teléfono.

El archivo de salida tiene que quedar así.

Código: Seleccionar todo
'011200190ABELLÁN ABELLÁN  CELIA     021200190097876541'
'011200191ABELLÁN LLOBREGATSANDRA    021200191087876544'
'011200192AGUILAR ESCRIBANOJAVIER    021200192089693803'
'011200193AGULLÓ CRESPO    ANA ISABEL                  '
'011200194ALBERO GARCÍA    ELENA     021200194098765432'
'011200195ALBERT SÁNCHEZ   CRISTINA  021200195098765232'
'011200196ALCARAZ BUADES   DAVID     021200196089768124'
'011200197ALDEGUER QUESADA SANTIAGO                    '
'011200198ALDEGUER SERRANO REBECA    021200198097521358'


Si un usuario no tiene teléfono se debe llenar con espacios la línea hasta completar el largo.

En el ejemplo los usuarios ID 1200193, 1200197 no tienen teléfono.

Esto es lo que estoy haciendo:

Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
#!/usr/bin/perl
open(DATA1,"input1.txt") or die "Error al abrir el archivo input1.txt";
open(DATA2,"input2.txt") or die "Error al abrir el archivo input2.txt";
open(DATA_MERGE,">>merge.txt") or die "Error al abrir el archivo merge";

@reg1 = <DATA1>;
@reg2 = <DATA2>;
$encontrado = '0';
 #Recorro el archivo input1.txt
 foreach $linea1 (@reg1)
 {
   # $elemento1 contiene el Id de usuario del archivo input1.txt
   $elemento1 = substr($linea1,2,7);
   #recorro desde el inicio el archivo input2.txt
   foreach $linea2 (@reg2)
   {
     $largo_linea2 = length($linea2);
     # $elemento2 contiene el Id de usuario del archivo input2.txt
     $elemento2 = substr($linea2,2,7);
     # Si los Is de usuario son iguales, imprimo en el archivo merge.txt
     # las dos lineas
     if ( $elemento1 eq $elemento2)
        {
         $encontrado = '1';
         chomp($linea1);
         print DATA_MERGE "$linea1$linea2";
         last;
        }
   }
   # Si no encuentro el Id de usuario en el input2.txt imprimo $linea1
   # concatenada con espacios en el archivo merge.txt
   if ( $encontrado eq '0')
    {
     chomp($linea1);
     substr($linea1,length($linea1),0)=" "x $largo_linea2 ."\n";
     print DATA_MERGE "$linea1";
    }
  $encontrado = '0';
 }

close(DATA1);
close(DATA2);
close(DATA_MERGE);
Coloreado en 0.004 segundos, usando GeSHi 1.0.8.4


En el archivo input1.txt siempre están todos los usuarios y el archivo input2.txt están solo los usuarios que tienen teléfono.

Quiero optimizar el script para que no recorra siempre desde el inicio el archivo input2.txt, ya que debería buscar desde la última línea que fue encontrada, ya que los registros están en orden. Si todos los usuarios tuvieran teléfonos, input1.txt e input2.txt tendrían la misma cantidad de registros y estarían en la misma fila.

¿Como puedo mejorar el script para que no recorra siempre desde el inicio el input2.txt?

PD: Las comillas son solo referenciales para el largo de la línea.

Saludos
xchidalgox
Perlero nuevo
Perlero nuevo
 
Mensajes: 23
Registrado: 2007-04-02 11:23 @516

Publicidad

Re: Buscar en un achivo

Notapor Perl user » 2007-04-08 15:28 @686

Antes de exponer mis comentarios sobre tu pregunta, me gustaría invitarte a replantear el diseño de tu secuencia de datos..., si te fijas aunque es sencillo el problema, ya comienzas a tener duplicidad de información, es decir, prácticamente lo que necesitas es hacer relaciones de datos, con algún dato existente en uno de los archivos (como el id del usuario). Es importante que reconsideres tu estrategia y comiences a utilizar alguna base de datos relacional, pequeña, tal como SQLite. Te ahorrará demasiado código y problemas de duplicidad de datos, aparte de tener que lidiar manualmente con tus archivos.

Ahora bien sobre tu pregunta original: Mira no sé que tan extensos son los datos que contienen tus archivos (hablo de la cantidad de registros), pero pensando siempre en escalabilidad de los sistemas, te recomiendo que en vez de cargar todo el contenido a memoria, considera el uso de Tie::File para que haga un posicionamiento inteligente sobre tu archivo utilizando el mismo código (es decir, utilizando un array para acceder el archivo). La diferencia será que en vez de tener todos los datos en memoria, lo que hará realmente es posicionamiento aleatorio en archivos por medio de un objeto (Tie).

Una vez aclarado ese punto, la manera en la que no necesitarás reposicionarte al inicio del array sería utilizando índices. Recorre el segundo array utilizando índices (desde 0 hasta $#reg2), almacenando siempre el índice actual, de esta manera en la siguiente iteración de tu primer ciclo (de reg1), se contemplará el indice almacenado para el for interno y no tienes que volver a procesar la misma información. Para esta parte es para dónde recomiendo el uso de un pequeño pero eficiente motor de bases de datos relacionales. A menos que tu quieras realizar una implementación de índice de datos.

Ahora bien, otra solución es que si los dos archivos tienen la misma cantidad de registros, entonces los leas concurrentemente y vayas insertando registros posicionales.

Otra recomendación que te puedo hacer es que si tus registros son de tamaño constante (que eso parece por la descripción de las posiciones de los elementos que diste arriba), entonces en vez de substr lo hagas con unpack, es mas eficiente y te evitas estar llamando substr múltiples veces, algo como esto:

Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
my $rec = '011200191ABELLÁN LLOBREGATSANDRA    ';
# 2 posiciones para el archivo, 7 para el id, 27 para el nombre
my ($file, $id, $name) = unpack "A2 A7 A27", $rec;
 
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4


Saludos, y nuevamente, replantea la estrategia.
Marco A. Manzo
[email protected]
http://www.unixmonkeys.com/amnesiac/
Perl Programming Language
Perl user
Maestro honorario
Maestro honorario
 
Mensajes: 271
Registrado: 2004-11-03 21:11 @924

Notapor xchidalgox » 2007-04-08 15:46 @698

El problema que he planteado es ficticio; es una simplificación de lo que realmente quiero hacer, pero la idea es la misma. No es de usuarios con números de teléfono. La información está en archivos planos SI o SI.

La cantidad de registros en el input1.txt son 50.000 aprox. y la cantidad de registros en el input2.txt es muy similar (igual o un poco menor).

Para este caso, solo si todos los usuarios tuvieran teléfonos los registros de los archivos serían iguales, cosa que no siempre se da.

Voy a investigar sobre Tie:File. Gracias.

¿ Otras ideas ?

Saludos.
xchidalgox
Perlero nuevo
Perlero nuevo
 
Mensajes: 23
Registrado: 2007-04-02 11:23 @516

Notapor Perl user » 2007-04-08 15:51 @702

Sí, échale un vistazo a SQLite.
Marco A. Manzo
[email protected]
http://www.unixmonkeys.com/amnesiac/
Perl Programming Language
Perl user
Maestro honorario
Maestro honorario
 
Mensajes: 271
Registrado: 2004-11-03 21:11 @924

Notapor xchidalgox » 2007-04-08 15:53 @703

No puedo usar BD solo archivos de texto plano.

Saludos
xchidalgox
Perlero nuevo
Perlero nuevo
 
Mensajes: 23
Registrado: 2007-04-02 11:23 @516

Notapor creating021 » 2007-04-08 16:23 @724

También se puede hacer con read y haciendo algo como esto:

Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
my ($id, $user);
(
  read(FILE_HANDLE, 0, 1) == 1,
  read(FILE_HANDLE, 2, <img src="http://perlenespanol.com/foro/images/smilies/icon_cool.gif" alt="8)" title="Cool" /> == 6
) or die "Fichero inapropiado\n";
 
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4


Haciendo dos subs (uno para leer el primer archivo y el otro para encontrar la respuesta) se puede hacer.

Pregunta: Cómo se podría hacer un cache del archivo? :shock:

PD: es mejor hacerlo como dice Perl user, con una DB.
Expect the worst, is it the least you can do?
Avatar de Usuario
creating021
Perlero frecuente
Perlero frecuente
 
Mensajes: 595
Registrado: 2006-02-23 16:17 @720
Ubicación: Frente al monitor

Notapor explorer » 2007-04-09 20:12 @883

Una posible solución es la comentada por Perl User: leer los ficheros en memoria y con unpack sacar las claves y luego unir los campos de las dos tablas.

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

# Leer los fichero, con un formato, a un hash con una determinada clave
sub lee_fichero {
    my ($fichero,$formato,$hash_ref,$clave) = @_;

    open(my $data,'<',$fichero) or die "ERROR: $!\n";

    while ( $linea = <$data> ) {
        chomp($linea);
        my @campos = unpack($formato, $linea);
        $hash_ref->{ $campos[$clave] } = $linea;
    }

    close $data;
}

lee_fichero('input1.txt', 'A2 A7 A28', \%data1, 1);
lee_fichero('input2.txt', 'A2 A7 A9 ', \%data2, 1);

# Unión de los dos ficheros
foreach my $clave ( keys %data1 ) {
    printf("%-38s%-18s\n", $data1{$clave}, $data2{$clave});
}
Coloreado en 0.002 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: 14480
Registrado: 2005-07-24 18:12 @800
Ubicación: Valladolid, España

Notapor explorer » 2007-04-11 04:03 @211

Tras conversación mantenida por chat, te cuento que hay información sobre el análisis de consumo de memoria de un programa aquí:
http://perldoc.perl.org/perldebguts.htm ... mory-usage

pero la opción recomendada sigue siendo usar Devel::Size.
JF^D Perl programming & Raku programming. Grupo en Telegram: https://t.me/Perl_ES
Avatar de Usuario
explorer
Administrador
Administrador
 
Mensajes: 14480
Registrado: 2005-07-24 18:12 @800
Ubicación: Valladolid, España


Volver a Básico

¿Quién está conectado?

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

cron