2015-09-21 6 views
-1

У меня есть два файла,Сравнивая данные в двух файлах в Perl

Файл A:

Folder name A 
    cp A 
    cp B 
Folder name B 
    cp D 
    cp F 

Файл B:

Folder name C 
    cp A 
    cp B 
Folder name A 
    cp A 
    cp B 
    cp C 
Folder name B 
    cp D 
    cp F 
Folder name D 
    cp A 
    cp D 

Вывод должен быть:

Folder name C: 
    cp A 
    cp B 
Folder name D 
    cp A 
    cp D 
Folder name A 
    cp C 

В принципе, я хочу проверить, есть ли совпадение в t его имя папки, а затем проверьте соответствие в имени cp для того же имени папки. Затем нам нужно удалить совпадения. Может ли кто-нибудь помочь мне, поскольку я новичок в perl.

У меня есть код, где он правильно дает имена папок, но удаляет некоторые из имен cp.

my %file2; 
open my $file2, '<', 'fileA.txt' or die "Couldnt open fileA.txt"; 
while (my $line = <$file2>) 
{ 
    ++$file2{$line}; 
} 
open my $file1, '<', 'fileB.txt' or die "Couldnt open fileB.txt"; 
while (my $line = <$file1>) 
{ 
    print $fh $line unless $file2{$line}; 
} 
+0

Мой сценарий правильно печатает имя папки, но как только он встречается с тем же именем cp в двух файлах, он удаляет это имя cp для всех папок. В приведенном выше вопросе мой скрипт дает результат как имя папки C и имя папки D. Но я хочу, чтобы результат был таким же, как описано в вопросе. – Tej

+0

Спасибо @Hunter McMillen за редактирование. – Tej

+3

Пожалуйста, покажите нам, что вы уже сделали. –

ответ

0

Есть две проблемы: анализ формата данных и сравнение. Вы не можете просто сравнивать файлы по строкам, ваш файл имеет структуру, и вам нужно проанализировать его в структуре данных Perl.

sub parse_file { 
    my $file = shift; 

    open my $fh, '<', $file; 

    my $in_folder; 
    my %folders =(); 

    while(<$fh>) { 
     # Entering a folder 
     if(/^Folder name (.*)\s*$/) { 
      $in_folder = $1; 
     } 
     # We're in a folder 
     elsif($in_folder) { 
      # Add a line to the folder actions 
      if(/^\s+(.*)\s*$/) { 
       push @{$folders{$in_folder}}, $1; 
      } 
      # We exited the folder but didn't enter another one 
      elsif(/^\S/) { 
       $in_folder = ''; 
      } 
     } 
    } 

    return \%folders; 
} 

Это много дополнительного кода для написания и отладки. Если ваши файлы были сохранены в виде YAML, JSON или XML, вы можете использовать библиотеку для этого.

Я намеренно отказался от форматирования и просто сохранил имена папок. Это упрощает работу с данными и защищает остальную часть кода от изменений форматирования.

Теперь каждый файл представляет собой хэш имен папок, которые содержат список команд.

 { 
     'A' => [ 
       'cp A', 
       'cp B' 
       ], 
     'B' => [ 
       'cp D', 
       'cp F' 
       ] 
     } 

Теперь мы должны их сравнить. Алгоритм таков:

  1. Если папка находится в одном файле, выберите ее.
  2. Если папка находится в обоих файлах, укажите различия (если есть).

К счастью, у нас есть Array::Utils, чтобы выполнить все необходимые пересечения и разницы для нас. Используйте array_diff, чтобы найти, какие папки находятся только в одном файле, и intersection, чтобы найти те, которые находятся в обоих. Затем используйте array_diff еще раз, чтобы найти различия.

sub compare_folders { 
    my($set1, $set2) = @_; 

    my @set1_names = keys %$set1; 
    my @set2_names = keys %$set2; 

    my %diffs; 

    # It's in one but not the other. 
    for my $name (array_diff @set1_names, @set2_names) { 
     $diffs{$name} = $set1->{$name} || $set2->{$name}; 
    } 

    # It's in both. 
    for my $name (intersect @set1_names, @set2_names) { 
     # They're different 
     if(my @diff = array_diff(@{$set1->{$name}}, @{$set2->{$name}})) { 
      $diffs{$name} = \@diff; 
     } 
    } 

    return \%diffs; 
} 

И, наконец, нам нужно отобразить результаты. Поскольку я решил сделать данные обобщенными и отформатировать форматирование, нам нужно вернуть его обратно.

sub display_folder { 
    my($name, $values) = @_; 

    my $display = "Folder name $name\n"; 

    for my $value (@$values) { 
     $display .= " $value\n" 
    } 

    return $display; 
} 

И все это вместе.

my @folders = map { parse_file($_) } @ARGV; 

my $diff = compare_folders(@folders); 

for my $name (keys %$diff) { 
    my $values = $diff->{$name}; 
    print display_folder($name, $values); 
} 
+0

Спасибо @Schwern. Но как отправить два файла? Как будто мне нужно прочитать данные из файла file.txt и fileB.txt. После разбора обоих файлов мне нужно их сравнить. Из кода мои папки = map {parse_file ($ _)} ARGV выполняет синтаксический анализ. Как отправить эти два файла? – Tej

+0

Также можно использовать ручное сравнение вместо использования Array :: Utils – Tej

+0

Вы не отправляете файлы в сценарии Unix. Вы передаете имена файлов в качестве аргументов, а в Perl вы читаете аргументы из '@ ARGV'; то вы передаете их подпрограмме, которая читает содержимое. Это происходит в последнем блоке кода. – reinierpost