В Windows, как сравнить два файла и вернуть только записи, отсутствующие во втором файле, которые изначально присутствовали в первом файле? - PullRequest
2 голосов
/ 06 мая 2009

Через равные промежутки времени мы получаем файлы CSV из внешнего источника, который мы почти не контролируем. Эти файлы представляют собой полные наборы текущих записей; однако, любые записи, которые были удалены с момента предыдущего, отсутствуют. Мы хотели бы сравнить два файла и создать отдельный файл удаленных записей, чтобы мы могли выполнить дополнительную обработку для них. В приложении в другой области у нас есть коммерческий пакет сортировки (CoSort), который делает это «из коробки»; однако, у нас нет доступа к этому здесь. Тем не менее, объемы не такие большие, и кажется, что это то, с чем стандартные или бесплатные инструменты могут справиться довольно легко. В идеале это может быть в форме пакетного файла Windows, но решения на Perl или awk тоже подойдут. Пример входных файлов:

Предыдущий файл:

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

Входящий файл:

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

Результирующий файл должен быть как минимум:

111,Name 111

Но если атрибуты из удаленных записей также проходят, это нормально.

До сих пор у меня есть пакетный файл, который использует бесплатную CMSort для сортировки двух файлов без записи заголовка, чтобы упростить процесс различного типа:

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

Но бит "сравнивать и показывать только пропущенные записи из первого файла" ускользает от меня. Отчасти сложность заключается в том, что среди оставшихся записей могут меняться многочисленные атрибуты, поэтому это не просто разница. Однако это похоже на специализированную команду diff, ограничивающуюся проверкой только ключевого поля, а не всей записи. Я не могу получить правильный синтаксис, хотя. Идеи? Количество записей не должно превышать 50 000 записей.

Примечание. Если бы это был SQL, а данные находились в таблицах, мы могли бы использовать оператор EXCEPT , но в этом случае перемещение данных в базу данных не является возможным.

Ответы [ 7 ]

4 голосов
/ 06 мая 2009
2 голосов
/ 06 мая 2009

если бы я делал это в Perl, я бы просто использовал пару хешей,


#!/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;

при условии, что ваши текстовые примеры находятся в файлах f1.txt и 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

1 голос
/ 06 мая 2009
#!/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 голосов
/ 06 мая 2009

Существует также Алгоритм :: Разница , доступный в CPAN.

0 голосов
/ 06 мая 2009

Давным-давно я работал над слабо похожей системой (на самом деле очень печально, почти 20 лет назад). Данные были сохранены в базе данных, и входящие данные нужно было сравнить с данными предыдущей недели. В данных было 74 столбца данных, и некоторые из них приняли форму пар «предыдущий код, предыдущая дата» (для нескольких циклов). Итак, один законный набор изменений был для:

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

В этом сценарии то, что было «name1, date1», стало «name3, date3»; по-видимому, в какой-то момент в прошлом произошла промежуточная смена имени и недавняя смена имени. Информация об изменении имени может быть отложена. Трудно было понять, что было общего с тем, что было раньше, и что было совершенно новым. Действительно, весь процесс был сложным.

Я фактически разработал двуязычную программу самовосстановления для управления этим материалом. В верхней части исходного кода использовались нотации комментариев на основном языке (Informix 4GL), в которых использовались как комментарии в стиле оболочки # ... eol, так и комментарии {...} (последние могли распространяться на несколько строк). Конечно, это также конструкция оболочки для перенаправления ввода / вывода, поэтому у меня был сценарий оболочки, который генерировал бы код I4GL, встроенный в комментарий {...}, и код был сгенерирован из таблицы, определяющей 70. + столбцы и то, как каждый из них должен быть обработан. Это сэкономило много времени на печать. ч

0 голосов
/ 06 мая 2009

Я бы использовал diff, и если он недоступен напрямую, используйте его через cygwin.

0 голосов
/ 06 мая 2009

Нетрудно написать небольшое консольное приложение, которое просматривает первый файл, анализирует ключи и проверяет второй файл на наличие строк, соответствующих ключам, а затем создает третий файл. Полагаю, я говорю, что это хороший случай, чтобы свернуть свое собственное. :) Кстати, это операция O (mn), где m, n - это размер файла 1 и файла 2, так что, вероятно, это будет не очень быстро.

...