• Publicidad

Comparar líneas en archivos de texto

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

Comparar líneas en archivos de texto

Notapor bvayap » 2015-09-30 11:56 @539

Buenas tardes.

Tengo que comparar un par de archivos de texto con la siguiente estructura:

Sintáxis: [ Descargar ] [ Ocultar ]
Using text Syntax Highlighting
Sexo;Estado;DNI;Nombre;Edad;Alias;Altura;Direccion;Hijos;Observaciones
Hombre;Soltero;11111111;Jose Aja Aja;50;pepe;2.00;calle 1;;Rubio
Mujer;Casada;22222222;Ana Aja Aje;65;anita;1.80;calle 3;2;Rubia
Mujer;Divorciada;33333333;Laura Asi Asa;38;laurita;1.70;calle 4;1;
Coloreado en 0.000 segundos, usando GeSHi 1.0.8.4


En el archivo nuevo puede haber cambiado la edad o dirección de algún registro, por lo que en el resultado debo guardar tanto la línea del archivo viejo como la del nuevo. Si no ha cambiado nada, no guardo nada en el resultado.

La estrategia que he seguido es la siguiente:
  1. leo el archivo viejo y lo meto en un hash
  2. abro el archivo nuevo y, para cada línea, veo si existe la clave en el hash anterior
    1. Si existe la clave, no guardo nada
    2. Si no existe, guardo la línea en un archivo intermedio llamado "Diferencias.txt"
  3. una vez tengo el archivo de diferencias completado, lo vuelvo a abrir y, para cada línea, leo un valor que no se repite, que es el "alias"
  4. abro el archivo viejo, busco la línea que contiene ese alias y la guardo en el resultado final "Comparado.txt" con el indicador "Archivo viejo" al inicio de la línea
  5. abro el archivo nuevo, busco la línea que contiene ese alias y la guardo en el resultado final "Comparado.txt" con el indicador "Archivo nuevo" al inicio de la línea.
De esta forma, en el archivo final "Comparado.txt" he guardado un registro del valor que tenía la línea en el archivo viejo y del que tiene ahora en el archivo nuevo.

El problema es que los archivos que estoy comparando tienen sobre 2000 líneas y he visto que hay "alias" que, a pesar de estar en el archivo intermedio "Diferencias.txt" han desaparecido en el archivo de resultados. Y algún "alias" que, a pesar de existir una sola vez en el archivo "Diferencias.txt", aparece duplicado en el resultado.

Como no tengo mucha experiencia con Perl, seguro que he metido la pata en algo. ¿Alguna sugerencia? Os dejo aquí el código empleado, por si a alguien le puede servir. Gracias de antemano.

Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. #!/usr/bin/perl
  2. use strict;                                    # estilo estricto
  3. use warnings;                                  # activar advertencias
  4. use diagnostics;                               # diagnósticos de fallos
  5. use autodie;                                   # «Es mejor morir que regresar con deshonor» --proverbio Klingon
  6.  
  7. system("cls");
  8. print "Buscamos diferencias entre dos ficheros txt.\n";
  9. print "Fichero Antiguo(sin extension): ";
  10. my $f1 = <>;
  11. chomp($f1);
  12. print "Fichero Nuevo(sin extension): ";
  13. my $f2 = <>;
  14. chomp($f2);
  15. my $ruta1 = "C:\\Documents and Settings\\Admin\\My Documents\\Perl\\Pruebas\\$f1\\";
  16. my $ruta2 = "C:\\Documents and Settings\\Admin\\My Documents\\Perl\\Pruebas\\$f2\\";
  17.  
  18. open my $afile, '<', $ruta1 . $f1 . "_limpio.txt"
  19.     or die "Couldn't open $f1.txt: $!";
  20.  
  21. my %a_lineas;
  22.  
  23. while ( my $linea1 = <$afile> ) {
  24.     chomp $linea1;
  25.     $a_lineas{$linea1} = 1;
  26. }
  27.  
  28. open my $SALIDA1, '>', $ruta2 . "Diferencias_" . $f1 . "_" . $f2 . ".txt";    # Escritura del archivo intermedio
  29. open my $bfile,   '<', $ruta2 . $f2 . "_limpio.txt"
  30.     or die "Couldn't open $f2.txt: $!";
  31.  
  32. while ( my $linea2 = <$bfile> ) {
  33.     chomp $linea2;
  34.     if ( exists $a_lineas{$linea2} ) {
  35.         next;
  36.     }
  37.     else {
  38.         print $SALIDA1 $linea2 . "\n";
  39.     }
  40. }
  41. close $SALIDA1;
  42. close $afile;
  43. close $bfile;
  44.  
  45. open my $diffile, '<', $ruta2 . "Diferencias_" . $f1 . "_" . $f2 . ".txt";
  46. open my $SALIDA2, '>', $ruta2 . "Comparado_"   . $f1 . "_" . $f2 . ".txt";
  47.  
  48. while ( my $linea3 = <$diffile> ) {
  49.     chomp $linea3;
  50.     my (@campos) = split ";", $linea3, 8;
  51.     my $alias = $campos[5];
  52.  
  53.     open my $cfile, '<', $ruta1 . $f1 . "_limpio.txt"
  54.         or die "Couldn't open $f1.txt: $!";
  55.     while ( my $linea4 = <$cfile> ) {
  56.         chomp $linea4;
  57.         if ( $linea4 =~ /$alias/ ) {
  58.             print $SALIDA2 "Fichero viejo;" . $linea4 . "\n";    # Escritura del fichero Resultados.
  59.             last;
  60.         }
  61.     }
  62.     close $cfile;
  63.  
  64.     open my $dfile, '<', $ruta2 . $f2 . "_limpio.txt"
  65.         or die "Couldn't open $f2.txt: $!";
  66.     while ( my $linea5 = <$dfile> ) {
  67.         chomp $linea5;
  68.         if ( $linea5 =~ /$alias/ ) {
  69.             print $SALIDA2 "Fichero nuevo;" . $linea5 . "\n";    # Escritura del fichero Resultados.
  70.             last;
  71.         }
  72.     }
  73.     close $dfile;
  74. }
  75. close $diffile;
  76. close $SALIDA2;
  77.  
Coloreado en 0.004 segundos, usando GeSHi 1.0.8.4
Última edición por explorer el 2015-09-30 14:08 @631, editado 1 vez en total
Razón: Formateado de código con Perltidy;
bvayap
Perlero nuevo
Perlero nuevo
 
Mensajes: 31
Registrado: 2013-05-31 02:42 @154

Publicidad

Re: Comparar líneas en archivos de texto

Notapor pierrot » 2015-10-01 12:12 @550

Si entendí bien, ambos ficheros tienen los mismos registros o tuplas, solo que en el segundo fichero el campo 'Dirección' o el campo 'Edad' de algunas de esas tuplas, fueron modificados. Si es así, tu algoritmo es correcto, aunque ineficiente. Si las tuplas están en el mismo orden en ambos ficheros, comparas línea a línea y si estuvieran en orden distinto (teniendo en cuenta que el alias es el identificador), podrías guardar dos hashes: %hash1 y %hash2 en los que la clave sea el alias (ya que es único) y el valor sea la línea correspondiente a ese alias. Luego recorres la lista de aliases y comparas los campos 'Dirección' y 'Edad' de $hash1{$alias} con los de $hash2{$alias} y si son distintos, imprimes.

En cuanto a tu código, probablemente el error esté en cómo determinas que el registro $linea corresponde al alias $alias. Lo haces mediante
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. $linea =~ /$alias/
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4


Imagínate que $alias es "Lau" y una de las líneas es:
Sintáxis: [ Descargar ] [ Ocultar ]
Using text Syntax Highlighting
Mujer;Casada;22222222;Ana Laura Almeida;33;anne;1.65;calle 3;2;Rubia
Coloreado en 0.000 segundos, usando GeSHi 1.0.8.4


Esa línea no se corresponde con una persona de alias 'Lau' (sino que su alias es 'anne') y sin embargo es matcheada por /Lau/. La regexp tendría que ser algo más sofisticada, por ejemplo,
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. $linea =~ /^ (?: .* ? ; ){5} $alias ;/x
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4

bvayap escribiste:
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. my %a_lineas;
  2.  
  3. while ( my $linea1 = <$afile> ) {
  4.     chomp $linea1;
  5.     $a_lineas{$linea1} = 1;
  6. }
  7.  
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4


Yo escribiría eso mismo de esta manera:
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. my %a_lineas = map {  chomp; $_=>1  } <$afile>
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4
Última edición por pierrot el 2015-10-02 06:11 @299, editado 2 veces en total
Avatar de Usuario
pierrot
Perlero nuevo
Perlero nuevo
 
Mensajes: 8
Registrado: 2015-09-08 12:53 @579

Re: Comparar líneas en archivos de texto

Notapor explorer » 2015-10-01 13:32 @606

En efecto, si guardas la información en hashes, no necesitas grabar y volver a leer el archivo de diferencias. Y el proceso será más rápido.

Te basta con hacer un split() usando el delimitador ';', y luego guardar la línea indexada por el alias (que se supone que es el valor que no se repite).

Lo que queda es recorrer las líneas del otro archivo y compararla con los valores almacenados. Si son líneas distintas, entonces es que hay algún cambio.

Al menos, eso entiendo que hay que hacer.

Por ejemplo, si tenemos estos dos archivos:
Sintáxis: (archivo_viejo.txt) [ Descargar ] [ Ocultar ]
Using text Syntax Highlighting
Sexo;Estado;DNI;Nombre;Edad;Alias;Altura;Direccion;Hijos;Observaciones
Hombre;Soltero;11111111;Jose Aja Aja;50;pepe;2.00;calle 1;;Rubio
Mujer;Casada;22222222;Ana Aja Aje;65;anita;1.80;calle 3;2;Rubia
Mujer;Divorciada;33333333;Laura Asi Asa;38;laurita;1.70;calle 4;1;
Coloreado en 0.000 segundos, usando GeSHi 1.0.8.4

Sintáxis: (archivo_nuevo.txt) [ Descargar ] [ Ocultar ]
Using text Syntax Highlighting
Sexo;Estado;DNI;Nombre;Edad;Alias;Altura;Direccion;Hijos;Observaciones
Hombre;Soltero;11111111;Jose Aja Aja;51;pepe;2.00;calle 1;;Rubio
Mujer;Casada;22222222;Ana Aja Aje;65;anita;1.80;calle 3;2;Rubia
Mujer;Divorciada;33333333;Laura Asi Asa;39;laurita;1.70;calle 41
Coloreado en 0.000 segundos, usando GeSHi 1.0.8.4

Con este programa:
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. #!/usr/bin/env perl
  2. use v5.14;
  3. use autodie;
  4.  
  5. #### Abrir archivos
  6. open my $VIEJO, '<', 'archivo_viejo.txt';
  7. open my $NUEVO, '<', 'archivo_nuevo.txt';
  8. open my $COMPA, '>', 'comparados.txt';
  9.  
  10. # Leer archivo viejo
  11. my %registros;
  12.  
  13. while (my $registro = <$VIEJO>) {
  14.     my $alias = (split /[;]/, $registro)[5];
  15.  
  16.     $registros{$alias} = $registro;
  17. }
  18.  
  19. # Hacer la comparación con el nuevo
  20. while (my $registro = <$NUEVO>) {
  21.     my $alias = (split /[;]/, $registro)[5];
  22.  
  23.     if (exists $registros{$alias}  and  $registros{$alias} eq $registro) {
  24.         # Los registros son iguales: no se hace nada
  25.     }
  26.     else {
  27.         # El registro no existe en el viejo o los registros son distintos
  28.         print $COMPA "Archivo viejo $registros{$alias}";
  29.         print $COMPA "Archivo nuevo $registro";
  30.     }
  31. }
  32.  
  33. #### Cerrar archivos
  34. close $VIEJO;
  35. close $NUEVO;
  36. close $COMPA;
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4
Sale este resultado:
Sintáxis: (comparados.txt) [ Descargar ] [ Ocultar ]
Using text Syntax Highlighting
Archivo viejo Hombre;Soltero;11111111;Jose Aja Aja;50;pepe;2.00;calle 1;;Rubio
Archivo nuevo Hombre;Soltero;11111111;Jose Aja Aja;51;pepe;2.00;calle 1;;Rubio
Archivo viejo Mujer;Divorciada;33333333;Laura Asi Asa;38;laurita;1.70;calle 4;1;
Archivo nuevo Mujer;Divorciada;33333333;Laura Asi Asa;39;laurita;1.70;calle 41
Coloreado en 0.000 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: Comparar líneas en archivos de texto

Notapor bvayap » 2015-10-01 14:33 @648

Gracias a ambos. Mañana intentaré probar vuestras soluciones.

La verdad es que anoche se me encendió la neurona y encontré una solución. No tengo el código delante, pero básicamente es:

1. Meto ambos archivos de texto en un hash cada uno.
2. Recorro el primer hash buscando si las claves existen en el segundo. Sí no existen, guardó la clave en un tercer hash de resultados.
3. Ídem recorriendo ahora el hash del segundo archivo y buscando las claves en el hash del primero.
4. Ordeno el hash de resultados.

Lo dicho, muchas gracias por vuestra aportación.
Saludos
bvayap
Perlero nuevo
Perlero nuevo
 
Mensajes: 31
Registrado: 2013-05-31 02:42 @154

Re: Comparar líneas en archivos de texto

Notapor pierrot » 2015-10-01 21:47 @949

¡Qué pulcritud inmaculada la del script de explorer! (válgase el pleonasmo). Lo único que cambiaría sería quizás
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. (split /[;]/, $registro)[5];
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4

por
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. (split /;/, $registro)[5];
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4

dado que ";" no es un metacaracter de las regexps como lo es, por ejemplo, un '.' (si fuera un punto, sí que justificaría poner '[.]' como alternativa a '\.').

Es mejor que lo yo había propuesto, dado que recorre el primer archivo para crear el hash %registros, y luego recorre el segundo archivo ya comparando con los registros antes guardados (yo había sugerido crear otro hash para el segundo archivo y luego hacer una tercer recorrida sobre los aliases comparando los registros de un hash con los del otro; es más eficiente lo que propone explorer).

bvayap escribiste:La verdad es que anoche se me encendió la neurona y encontré una solución. No tengo el código delante, pero básicamente es:

1. Meto ambos archivos de texto en un hash cada uno.
2. Recorro el primer hash buscando si las claves existen en el segundo. Sí no existen, guardó la clave en un tercer hash de resultados.
3. Ídem recorriendo ahora el hash del segundo archivo y buscando las claves en el hash del primero.
4. Ordeno el hash de resultados.


Yo supuse que la cantidad de registros en ambos ficheros era la misma y si hay un registro con alias 'X' en el fichero viejo, hay un registro con alias 'X' en el fichero nuevo. En cambio, explorer va un poco más allá y muestra también en el resultado los registros que están en el fichero nuevo y no están en el viejo (pero NO muestra los que están en el viejo y no están en el nuevo). No obstante, en la parte del if donde comenta "# Los registros son iguales: no se hace nada", se puede borrar del hash ese registro con lo que al final quedarían en él sólo los que están en el viejo y no están en el nuevo (y puedes desplegarlos al final). Tu solución es también correcta, y se puede optimizar teniendo en cuenta lo siguiente: si sabes que ciertas claves del primer hash están entre las claves del segundo hash, puedes excluirlas de la lista cuando te fijas en qué claves del segundo hash están entre las del primero (con lo cual te quedarán las que sabes de antemano que no están).
Avatar de Usuario
pierrot
Perlero nuevo
Perlero nuevo
 
Mensajes: 8
Registrado: 2015-09-08 12:53 @579

Re: Comparar líneas en archivos de texto

Notapor bvayap » 2015-10-02 05:52 @286

:D A veces pienso que explorer inventó Perl :D

Bueno, aquí os dejo mi código para que sea "criticado" :P

Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. #!/usr/bin/perl
  2. use strict;                                     # estilo estricto
  3. use warnings;                                   # activar advertencias
  4. use diagnostics;                                # diagnósticos de fallos
  5. use autodie;                                    # Es mejor morir que regresar con deshonor --proverbio Klingon
  6.  
  7. system ("cls");
  8. print "Buscamos diferencias entre dos ficheros txt.\n";
  9. print "Fichero Antiguo(sin extensión): ";
  10. my $f1 = <>;
  11. chomp ($f1);
  12. print "Fichero Nuevo(sin extensión): ";
  13. my $f2 = <>;
  14. chomp ($f2);
  15.  
  16. # Indicamos la ruta para los ficheros viejo y nuevo
  17. my $ruta1 = "C:\\Documents and Settings\\Admin\\My Documents\\Perl\\Pruebas\\$f1\\";
  18. my $ruta2 = "C:\\Documents and Settings\\Admin\\My Documents\\Perl\\Pruebas\\$f2\\";
  19.  
  20. # Abrimos ambos ficheros y los metemos en memoria
  21. open my $a_file, '<', $ruta1.$f1."_limpio.txt"
  22.     or die "Couldn't open $f1.txt: $!";
  23. open my $b_file, '<', $ruta2.$f2."_limpio.txt"
  24.     or die "Couldn't open $f2.txt: $!";
  25.  
  26. # Definimos los Hashes de entrada y salida
  27. my %a_lineas;
  28. my %b_lineas;
  29. my %results;
  30.  
  31. # Metemos cada fichero en su Hash
  32. while (my $linea1 = <$a_file>) {
  33.     chomp $linea1;
  34.     $a_lineas{$linea1} = "Fichero Viejo;";
  35. }
  36. while (my $linea2 = <$b_file>) {
  37.     chomp $linea2;
  38.     $b_lineas{$linea2} = "Fichero Nuevo;";
  39. }
  40.  
  41. # Recorremos el fichero nuevo para ver si la línea existe en el viejo
  42. my $a;
  43. my $b;
  44. foreach $b (keys %b_lineas) {
  45.         if (exists $a_lineas {$b}) {
  46.                 next;
  47.                 } else {
  48.                 $results {$b} = "Fichero nuevo;";
  49.         }
  50. }
  51.  
  52. # Recorremos el fichero viejo para ver si la línea existe en el nuevo
  53. foreach $a (keys %a_lineas) {
  54.         if (exists $b_lineas {$a}) {
  55.                 next;
  56.                 } else {
  57.                 $results {$a} = "Fichero viejo;";
  58.         }
  59. }
  60.  
  61. # Ahora guardamos el Hash con las diferencias en un fichero de texto para la salida.
  62. open my $SALIDA,  '>', $ruta2."Comparado_".$f1."_".$f2.".txt";               # Escritura del archivo resultado
  63. my $r;
  64. foreach $r (sort (keys (%results))) {
  65.         print $SALIDA ($results {$r}).$r."\n";
  66.         }
  67.  
  68. # Cerramos los ficheros
  69. close $a_file;
  70. close $b_file;
  71. close $SALIDA;
Coloreado en 0.002 segundos, usando GeSHi 1.0.8.4
bvayap
Perlero nuevo
Perlero nuevo
 
Mensajes: 31
Registrado: 2013-05-31 02:42 @154

Re: Comparar líneas en archivos de texto

Notapor pierrot » 2015-10-02 07:31 @355

bvayap escribiste:
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. # Metemos cada fichero en su Hash
  2. while (my $linea1 = <$a_file>) {
  3.     chomp $linea1;
  4.     $a_lineas{$linea1} = "Fichero Viejo;";
  5. }
  6. while (my $linea2 = <$b_file>) {
  7.     chomp $linea2;
  8.     $b_lineas{$linea2} = "Fichero Nuevo;";
  9. }
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4


¿Por qué en el valor de los hashes guardas "Fichero Viejo;" o "Fichero Nuevo;" si después no usas esa información en ningún momento? Esto es cuestión de gustos, pero lo mismo que escribiste en forma de dos bucles, puede ser reescrito así:
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. my %a_lineas = map {  chomp; $_=>"Fichero Viejo;"  } <$a_file>
  2. my %b_lineas = map {  chomp; $_=>"Fichero Nuevo;"  } <$b_file>
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4


bvayap escribiste:
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. # Recorremos el fichero nuevo para ver si la línea existe en el viejo
  2. my $a;
  3. my $b;
  4. foreach $b (keys %b_lineas) {
  5.         if (exists $a_lineas {$b}) {
  6.                 next;
  7.         } else {
  8.                 $results {$b} = "Fichero nuevo;";
  9.         }
  10. }
  11.  
  12. # Recorremos el fichero viejo para ver si la línea existe en el nuevo
  13. foreach $a (keys %a_lineas) {
  14.         if (exists $b_lineas {$a}) {
  15.                 next;
  16.         } else {
  17.                 $results {$a} = "Fichero viejo;";
  18.         }
  19. }
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4


Como dije en mi anterior mensaje, la segunda recorrida es innecesaria:
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. # Recorremos el fichero nuevo para ver si la línea existe en el viejo
  2. my $a;
  3. my $b;
  4. foreach $b (keys %b_lineas) {
  5.         if (exists $a_lineas {$b}) {
  6.                 delete $a_lineas{$b};
  7.         } else {
  8.                 $results {$b} = "Fichero nuevo;";
  9.         }
  10. }
  11.  
  12. # Ahora %a_lineas contiene todos los registros que existen en el viejo y
  13. # no existen en el nuevo. Agrego estos registros directamente a %results
  14. %results = (%results, %a_lineas);
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4


Otro pequeño detalle es que en tu archivo de resultados quizá en un caso aparece la línea del fichero nuevo y luego la del viejo, y en otro caso al revés (porque despliegas por orden ASCII).

Me parece mejor modificar ligeramente el script de explorer:
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. #!/usr/bin/env perl
  2. use v5.14;
  3. use autodie;
  4.  
  5. #### Abrir archivos
  6. open my $VIEJO, '<', 'archivo_viejo.txt';
  7. open my $NUEVO, '<', 'archivo_nuevo.txt';
  8. open my $COMPA, '>', 'comparados.txt';
  9.  
  10.  
  11. my %registros;
  12. my %diferencias;
  13.  
  14. # Leer archivo viejo
  15. while (my $registro = <$VIEJO>) {
  16.     chomp $registro;
  17.     my $alias = (split /;/, $registro)[5];
  18.  
  19.     $registros{$alias} = $registro;
  20. }
  21.  
  22. # Hacer la comparación con el nuevo
  23. while (my $registro = <$NUEVO>) {
  24.     chomp $registro;
  25.     my $alias = (split /;/, $registro)[5];
  26.  
  27.     if ( exists $registros{$alias} ) {
  28.         # Existe el registro en ambos ficheros    
  29.         if ($registros{$alias} ne $registro) {
  30.             # Si los registros son diferentes, agregar a %diferencias
  31.             $diferencias{$alias} = "Archivo viejo $registros{$alias}\n" . "Archivo nuevo $registro\n";
  32.         }
  33.         # Borrar la entrada en %registros así quedan las que están en el viejo exclusivamente
  34.         delete $registros{$alias};
  35.     } else {
  36.         # El registro no existe en el viejo
  37.         $diferencias{$alias} = "Archivo nuevo $registro\n";
  38.     }
  39. }
  40.  
  41. # Agregar las líneas que están en el viejo y no están en el nuevo
  42. foreach( keys %registros ) {
  43.     $diferencias{$_} = "Archivo viejo $registros{$_}\n";
  44. }
  45.  
  46. # Imprimir resultados ordenados por alias
  47. foreach( sort keys %diferencias ) {
  48.     print $COMPA $diferencias{$_};
  49. }
  50.  
  51. #### Cerrar archivos
  52. close $VIEJO;
  53. close $NUEVO;
  54. close $COMPA;
Coloreado en 0.002 segundos, usando GeSHi 1.0.8.4
Avatar de Usuario
pierrot
Perlero nuevo
Perlero nuevo
 
Mensajes: 8
Registrado: 2015-09-08 12:53 @579

Re: Comparar líneas en archivos de texto

Notapor explorer » 2015-10-02 16:16 @719

Si yo no hago chomp(), será por algo ;)

El ponerle corchetes al ';' es para que... se note más. Pero... aún se puede hacer más escandaloso (Regla "Separated Data" del PBP, pág. 157). Ver código más abajo.

No es recomendable el uso de las variables $a y $b en cualquier código, ya que son "especiales" para el caso de los miniprogramas de sort{}.

Si se usa un Perl igual o superior a v5.12, el modo 'strict' y 'warnings' están activados por defecto. Por eso con una línea use v5.14;, basta para tener un entorno de desarrollo.

Si se usa 'autodie', no es necesario poner la coletilla 'or die "...";' en los open(), ya que, precisamente, ese es su trabajo.

No se pueden recorrer los registros usándolos como claves, ya que la clave del asunto es el 'alias', por ser el identificador único en los registros. Si no lo hacemos, dos registros serán "distintos" cuando haya un cambio pequeño (edad, por ejemplo).

La barra diagonal inversa '\' se suele usar para escapar, pero no siempre es necesario escapar... (ver código siguiente).

Es mejor usar #!/usr/bin/env perl ya que así podemos usar un perl distinto de el del sistema.

Otra solución, con otra forma de filtrar los registros:
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. #!/usr/bin/env perl
  2. use v5.14;
  3. use diagnostics;                                        # diagnósticos de fallos
  4. use autodie;                                            # Es mejor morir que regresar con deshonor --proverbio Klingon
  5.  
  6. my $FIELD_SEPARATOR = q{;};                             # separador de campos de los registros
  7.  
  8. system ($^O eq 'linux' ? 'clear' : 'cls');              # según el sistema operativo, borramos la pantalla
  9.  
  10. say 'Buscamos diferencias entre dos ficheros txt.';     # un say() es lo mismo que un print "...\n", pero más corto
  11.  
  12. print "Fichero Viejo (sin extensión): ";
  13. chomp(my $viejo = <>);                                  # es mejor usar variables 'con sentido'
  14.  
  15. print "Fichero Nuevo (sin extensión): ";
  16. chomp(my $nuevo = <>);
  17.  
  18. # Ruta a los archivos
  19. my $RUTA          = 'C:\Documents and Settings\Admin\My Documents\Perl\Pruebas';
  20. my $ARCHIVO_VIEJO = $RUTA . '\\' . $viejo . '\\' . $viejo . '_limpio.txt';
  21. my $ARCHIVO_NUEVO = $RUTA . '\\' . $nuevo . '\\' . $nuevo . '_limpio.txt';
  22. my $ARCHIVO_COMPA = $RUTA . '\\' . $nuevo . '\\' . 'Comparado_' . $viejo . '_' . $nuevo . '.txt';
  23.  
  24. # Leemos ambos archivos
  25. open my $VIEJO, '<', $ARCHIVO_VIEJO;
  26. open my $NUEVO, '<', $ARCHIVO_NUEVO;
  27.  
  28. <$VIEJO>;                                               # despreciamos las cabeceras
  29. <$NUEVO>;
  30.  
  31. # Hash: Las claves son los alias; los valores son los registros
  32. my %viejos = map { (split $FIELD_SEPARATOR)[5] => $_ } <$VIEJO>;
  33. my %nuevos = map { (split $FIELD_SEPARATOR)[5] => $_ } <$NUEVO>;
  34.  
  35. close $VIEJO;
  36. close $NUEVO;
  37.  
  38. #use Data::Dumper;
  39. #say Dumper \%viejos;
  40. #say Dumper \%nuevos;
  41.  
  42. # Nos quedamos con los registros realmente nuevos
  43. # Aquellos que no existen en %viejos o que son distintos entre el nuevo y el viejo
  44. %nuevos = map  { $_ => $nuevos{$_} }
  45.           grep { not exists $viejos{$_}  or  $viejos{$_} ne $nuevos{$_} }
  46.           keys %nuevos;
  47.  
  48. #say Dumper \%viejos;
  49. #say Dumper \%nuevos;
  50.  
  51. # Salida de diferencias
  52. open my $SALIDA, '>', $ARCHIVO_COMPA;
  53.  
  54. for my $alias ( sort keys %viejos ) {                   # recorremos los reg. viejos
  55.  
  56.     print $SALIDA "Archivo viejo: $viejos{$alias}";
  57.  
  58.     if ( exists $nuevos{ $alias } ) {                   # si hay un reg. nuevo
  59.  
  60.         print $SALIDA "Archivo nuevo: $nuevos{$alias}";
  61.         delete $nuevos{$alias};                         # lo quitamos porque ya lo hemos publicado
  62.     }
  63. }
  64.  
  65. for my $registro (values %nuevos) {                     # recorremos los reg. nuevos que quedan
  66.  
  67.     print $SALIDA "Archivo nuevo: $registro";
  68. }
  69.  
  70. close $SALIDA;
Coloreado en 0.002 segundos, usando GeSHi 1.0.8.4
La salida es:
Sintáxis: [ Descargar ] [ Ocultar ]
Using text Syntax Highlighting
Archivo viejo: Mujer;Casada;22222222;Ana Aja Aje;65;anita;1.80;calle 3;2;Rubia
Archivo viejo: Mujer;Divorciada;33333333;Laura Asi Asa;38;laurita;1.70;calle 4;1;
Archivo nuevo: Mujer;Divorciada;33333333;Laura Asi Asa;39;laurita;1.70;calle 41
Archivo viejo: Hombre;Soltero;11111111;Jose Aja Aja;50;pepe;2.00;calle 1;;Rubio
Archivo nuevo: Hombre;Soltero;11111111;Jose Aja Aja;51;pepe;2.00;calle 1;;Rubio
Archivo nuevo: Hombre;Casado;8878787878;Antonio Luengo;40;Toni;3.00;calle 12;;Caso
Coloreado en 0.000 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


Volver a Básico

¿Quién está conectado?

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

cron