2009-05-05 12 views
2

À intervalles réguliers, nous recevons des fichiers CSV provenant d'une source externe sur laquelle nous avons peu de contrôle. Ces fichiers sont des ensembles complets d'enregistrements en cours; cependant, tous les enregistrements qui ont été supprimés depuis le précédent ne sont pas présents. Nous aimerions comparer les deux fichiers et créer un fichier distinct d'enregistrements supprimés afin que nous puissions effectuer un traitement supplémentaire sur eux. Dans une application dans un autre domaine, nous avons un paquet de tri commercial (CoSort) qui le fait hors de la boîte; Cependant, nous n'y avons pas accès ici. Les volumes ne sont cependant pas très importants, et il semble que ce soit quelque chose que les outils standards ou gratuits puissent gérer assez facilement. Idéalement, cela prendrait la forme d'un fichier de commandes Windows, mais les solutions Perl ou awk seraient également acceptables. Exemple les fichiers d'entrée:Sous Windows, comment comparer deux fichiers et ne renvoyer que les enregistrements manquants dans le second fichier qui étaient initialement présents dans le premier fichier?

Fichier précédent:

X_KEY,X_NAME,X_ATTRIBUTE 
123,Name 123,ATT X 
111,Name 111,ATT X 
777,Name 777,ATT Y 

fichier entrant:

X_KEY,X_NAME,X_ATTRIBUTE 
777,Name 777,ATT Y 
123,Name 123,ATT CHANGED 

résultant fichier doit être au minimum:

111,Name 111 

Mais si les attributs de la suppression les enregistrements viennent aussi, c'est bien.

Jusqu'à présent, je dispose d'un fichier batch qui utilise CMSort freeware pour trier les deux fichiers moins l'en-tête pour le rendre plus facile pour un certain type de processus diff:

REM Sort Previous File, Skip Header 

C:\Software\CMSort\cmsort.exe /H=1 x_previous.txt x_previous_sorted.txt 

REM Sort Incoming File, Skip Header 

G:\Software\CMSort\cmsort.exe /H=1 x_incoming.txt x_incoming_sorted.txt 

Mais le « comparer et montrer que la les enregistrements manquants du premier fichier 'm'échappent. Une partie de la complexité est que de nombreux attributs peuvent changer parmi les enregistrements qui restent, donc ce n'est pas un diff pur. Cependant, cela ressemble à une commande de diff spécialisée - celle qui se limite à vérifier uniquement le champ de la clé, pas l'ensemble de l'enregistrement. Cependant, je n'arrive pas à avoir la syntaxe correcte. Des idées? Le nombre d'enregistrements ne doit pas dépasser 50 000 enregistrements.

Remarque: Si c'était SQL et que les données étaient dans des tables, nous pourrions utiliser le EXCEPT operator mais le déplacement des données vers la base de données dans ce cas n'est pas une option.

+2

puisque vous mentionnez diff, il est intéressant de noter que diffutils est disponible pour windows : http: //gnuwin32.sourc eforge.net/packages/diffutils.htm – si28719e

Répondre

2

si je devais le faire en perl Je venais d'utiliser quelques hash,


#!/usr/bin/perl -w                           
use strict; 
use warnings; 

my %orig; my %new; my %changed; 

open(F1,"<$ARGV[0]")||die"Couldn't open $ARGV[0]: $!\n"; 
while(<F1>){ 
    chomp; 
    @_ = split(/,/); 
    $orig{$_[0]} = $_; 
} 
close(F1); 

open(F2,"<$ARGV[1]")||die"Couldn't open $ARGV[1]: $!\n"; 
while(<F2>){ 
    chomp; 
    @_ = split(/,/); 
    if($orig{$_[0]}){ 
     if($orig{$_[0]} ne $_){ 
      $changed{$_[0]} = $orig{$_[0]}."||".$_; 
     } 
     delete $orig{$_[0]}; 
    }else{ 
     $new{$_[0]} = $_; 
    } 
} 
close(F2); 

print "Deleted:\n"; 
print map{$orig{$_}."\n"} sort {$b<=>$a} keys %orig; 
print "Added:\n"; 
print map{$new{$_}."\n"} sort {$b<=>$a} keys %new; 
print "Changed:\n"; 
print map{$changed{$_}."\n"} sort {$b<=>$a} keys %changed; 

vos exemples en supposant texte sont dans des fichiers f1.txt et f2.txt,


kettle$ ./compare.pl f1.txt f2.txt 

Deleted: 
111,Name 111,ATT X 
Added: 
Changed: 
123,Name 123,ATT X||123,Name 123,ATT CHANGED 

0

Il ne serait pas difficile d'écrire une petite application de console qui a traversé le premier fichier, analysé les clés et vérifié le deuxième fichier pour les lignes correspondant aux clés, puis créer le troisième fichier. Je suppose que je dis que cela semble être un bon cas pour rouler le vôtre. :) Soit dit en passant, il s'agit d'une opération O (mn) où m, n sont la taille du fichier 1 et du fichier 2, donc il ne sera probablement pas très rapide.

1
#!/usr/bin/perl 

use strict; 
use warnings; 

@ARGV == 2 or die "mycompare oldfile newfile\n"; 

my ($oldfile, $newfile) = @ARGV; 

my %newrecords; 

open my $new, '<', $newfile 
    or die "Cannot open '$newfile':$!"; 

scalar <$new>; # skip header 

while (my $line = <$new>) { 
    next unless $line =~ /\S/; 
    my ($record) = split /,/, $line; 
    $newrecords{ $record } = 1; 
} 

close $new; 

open my $old, '<', $oldfile 
    or die "Cannot open '$oldfile': $!"; 

scalar <$old>; # skip header 

while (my $line = <$old>) { 
    next unless $line =~ /\S/; 
    my ($record) = split /,/, $line; 
    print $line unless exists $newrecords{ $record }; 
} 

close $new; 

__END__ 

C:Temp> mycompare old.csv new.csv 
111,Name 111,ATT X 
0

Je voudrais utiliser diff, et si ce n'est pas disponible directement, utilisez-le via cygwin.

4
+0

Les scripts Perl laissés par blackkettle et Sinan font tous les deux le tour pour moi (merci beaucoup!), mais c'est plus dans le sens de ce que je cherchais à l'origine. Cependant, avec gnuwin32 grep, j'obtiens une erreur en essayant^[\>]. Je ne vois pas non plus comment vous allez éliminer les enregistrements modifiés des fichiers supprimés (puisque les deux apparaîtront avec <). –

+0

Vous devriez mettre l'argument à grep entre guillemets, car^et> ont une signification particulière pour cmd. – Joey

+0

Johannes, merci, cela élimine l'erreur pour moi; Cependant, je ne pense pas que cela concerne le deuxième point. Ce grep particulier renvoie à la fois les enregistrements supprimés et les enregistrements modifiés. Je voudrais voir seulement les enregistrements supprimés. –

0

je travaillais sur un système similaire légèrement une fois sur depuis longtemps (distressingly il y a près de 20 ans, en fait). Les données étaient stockées dans une base de données et les données entrantes devaient être comparées aux données de la semaine précédente. Il y avait 74 colonnes de données dans les données, et certaines d'entre elles prenaient la forme de paires «code précédent, date antérieure» (pour plusieurs cycles). Ainsi, un ensemble légitime de changements a été pour:

Old: 
name1: value1  date1: 2000-01-01 
name2: value2  date2: 1995-05-31 
name3: value3  date3: 1990-10-13 

New: 
name1: New Name  date1: 2009-03-31 
name2: Other Name date2: 2005-04-12 
name3: value1  date3: 2000-01-01 

Dans ce scénario, ce qui était « nom1, date1 » est devenu « NAME3, date3 »; il semble qu'il y ait eu un changement de nom intermédiaire à une date antérieure et un changement de nom récent. Les renseignements sur les changements de noms pourraient être retardés. Travailler sur ce qui était commun avec ce qui s'est passé avant et ce qui était tout neuf était difficile. En effet, tout le processus était délicat.

J'ai effectivement développé un programme auto-régénérant bilingue pour gérer ce genre de choses. La partie supérieure du code source a profité des notations de commentaires dans la langue principale (Informix 4GL) qui utilisait à la fois les commentaires # ... eol de style shell et les commentaires {...} (ce dernier pouvant s'étaler sur plusieurs lignes. Bien sûr, il s'agit également d'une structure de shell pour la redirection d'E/S. J'ai donc le script shell qui génèrerait le code I4GL incorporé dans un commentaire {...} et le code a été généré à partir d'une table définissant les 70 + colonnes et la façon dont chacun avait besoin d'être manipulé. Cela a sauvé beaucoup de taper au fil du temps. h