Как вставить разные строки значений в хэши и сравнить их с циклом foreach - PullRequest
0 голосов
/ 18 октября 2018

У меня есть два файла, мне нужно сделать сравнение, чтобы узнать совпадающие и несоответствующие данные.У меня сейчас две проблемы:

Вопрос 1: один из моих хэшей может захватывать только 2-й ряд 'num', я пытался использовать

push @{hash1{name1}},$x1,$y1,$x2,$y2

, но он все еще возвращает2-й ряд 'num'.

Файл1:

name    foo
num     111 222 333 444
name    jack
num     999 111 222 333
num     333 444 555 777

Файл2:

name    jack
num     999 111 222 333
num     333 444 555 777
name    foo
num     666 222 333 444

Это мой код:

#!/usr/bin/perl
use strict;
use warnings;
use Data::Dumper;

my $input1=$ARGV[0];
my $input2=$ARGV[1];

my %hash1;
my %hash2;
my $name1;
my $name2;
my $x1;
my $x2;
my $y2;
my $y1;

open my $fh1,'<', $input1 or die "Cannot open file : $!\n";
while (<$fh1>)
{   
    chomp;
    if(/^name\s+(\S+)/)
    {   
        $name1 = $1; 
    }   
    if(/^num\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)/)
    {   
        $x1 = $1; 
        $y1 = $2; 
        $x2 = $3; 
        $y2 = $4; 
    }
    $hash1{$name1}=[$x1,$y1,$x2,$y2];
}   
close $fh1;
print Dumper (\%hash1);

open my $fh2,'<', $input2 or die "Cannot open file : $!\n";
while (<$fh2>)
{   
    chomp;
    if(/^name\s+(\S+)/)
    {
        $name2 = $1; 
    }
    if(/^num\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)/)
    {
        $x1 = $1; 
        $y1 = $2; 
        $x2 = $3;
        $y2 = $4;
    }

    $hash2{$name2}=[$x1,$y1,$x2,$y2];

}
close $fh2;
print Dumper (\%hash2);

Мой вывод:

$VAR1 = {
          'jack' => [
                      '333',
                      '444',
                      '555',
                      '777'
                    ],
          'foo' => [
                     '111',
                     '222',
                     '333',
                     '444'
                   ]
        };
$VAR1 = {
          'jack' => [
                      '333',
                      '444',
                      '555',
                      '777'
                    ],
          'foo' => [
                     '666',
                     '222',
                     '333',
                     '444'
                   ]
        };

Мой ожидаемый вывод:

$VAR1 = {
          'jack' => [ 
                      '999',
                      '111',
                      '222',
                      '333',
                      '333',
                      '444',
                      '555',
                      '777'
                    ],
          'foo' => [
                     '111',
                     '222',
                     '333',
                     '444'
                   ]
        };
$VAR1 = {
          'jack' => [ 
                      '999',
                      '111',
                      '222',
                      '333',
                      '333',
                      '444',
                      '555',
                      '777'
                    ],
          'foo' => [
                     '666',
                     '222',
                     '333',
                     '444'
                   ]
        };

Вопрос 2: Я попытался использовать этот цикл foreach для сопоставления ключей и значений и распечатать в табличном формате,Я попробовал это:

print "Name\tx1\tX1\tY1\tX2\tY2\n"
foreach my $k1(keys %hash1)
{
    foreach my  $k2 (keys %hash2)
    {
        if($hash1{$name1} == $hash2{$name2})
        {
            print "$name1,$x1,$y1,$x2,$y2"
        }
    }
}

, но я получаю:

"my" variable %hash2 masks earlier declaration in same scope at script.pl line 67.
"my" variable %hash1 masks earlier declaration in same scope at script.pl line 69.
"my" variable $name1 masks earlier declaration in same scope at script.pl line 69.
"my" variable %hash2 masks earlier declaration in same statement at script.pl line 69.
"my" variable $name2 masks earlier declaration in same scope at script.pl line 69.
syntax error at script.pl line 65, near "$k1("
Execution of script.pl aborted due to compilation errors.

желаемый результат для соответствия:

Name     x1   y1   x2   y2
jack     999  111  222  333
         333  444  555  777

1 Ответ

0 голосов
/ 18 октября 2018

Единственная прямая ошибка заключается в том, что вы присваиваете элементу хеша значение $hash2{$name2}=[...], которое перезаписывает все, что раньше было у этого ключа.Таким образом, ваш вывод показывает для jake только второй набор чисел.Вам нужно нажать на этот arrayref.Некоторые комментарии к коду приведены ниже.

Вот элементарный (но работающий) код.Пожалуйста, отметьте и внедрите пропущенные проверки.

use warnings;
use strict;
use feature 'say';

my ($f1, $f2) = @ARGV;
die "Usage: $0 file1 file2\n"  if not $f1 or not $f2;

my $ds1 = read_file($f1);
my $ds2 = read_file($f2);

compare_data($ds1, $ds2);

sub compare_data {
    my ($ds1, $ds2) = @_;    
    # Add: check whether one has more keys; work with the longer one
    foreach my $k (sort keys %$ds1) {
        if (not exists $ds2->{$k}) {
            say "key $k does not exist in dataset 2";
            next;
        }   
        # Add tests: do both datasets have the same "ref" type here?
        # If those are arrayrefs, as expected, are they the same size?

        my @data = @{$ds1->{$k}};
        foreach my $i (0..$#data) {
            if ($data[$i] ne $ds2->{$k}->[$i]) {
                say "differ for $k: $data[$i] vs $ds2->{$k}->[$i]";
            }
        }   
    }
}

sub read_file {
    my ($file) = @_; 
    open my $fh, '<', $file or die "Can't open $file: $!";
    my (%data, $name);
    while (<$fh>) {
        my @fields = split;
        if ($fields[0] eq 'name') {
            $name = $fields[1];
            next;
        }
        elsif ($fields[0] eq 'num') {
            push @{$data{$name}}, @fields[1..$#fields];
        }
    }   
    return \%data;
}

Я оставляю это в качестве упражнения для кодирования желаемого формата распечатки.Выше напечатаны

differ for foo: 111 vs 666

Обратите внимание на комментарии в коде, чтобы добавить тесты.Когда вы спускаетесь в структуры данных, чтобы сравнить их, вам необходимо проверить, переносят ли они данные одного и того же типа на каждом уровне (см. ref ) и имеют ли они одинаковый размер (чтобы вы не пыталисьчитать за конец массива).Как только вы получите такую ​​работу под вашим поясом, найдите модули для этого.

Я использую eq для сравнения данных (в arrayrefs), так как твердо не указано, что они являются числами.Но если это так, как представляется, измените eq на ==.

Выполнение проверки кода приведет нас слишком далеко, но вот несколько замечаний

  • Когда вы поймаете себя на том, что нуждаетесь в таком длинном списке переменных, подумайте «коллекции» и пересмотрите свой выбор структур данных для решения проблемы.Обратите внимание, что в приведенном выше примере мне не нужна была одна скалярная переменная для данных (я использовал одну для временного хранения имени)

  • Выбор строк с помощью регулярного выражения является неотъемлемой частьюанализа текста - когда подходит.Ознакомьтесь с другими подходами.На данный момент см split

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...