Обход структуры данных Perl - ссылка следует за ключом - PullRequest
0 голосов
/ 03 октября 2011

Результат: много строк HASH(0x1948958) ARRAY(0x1978250) ./directory/filename

Желаемый результат: [Key of first hash] [Key of second hash] ./directory/filename #(elements of array, currently working)

Catch: должен переноситься на N структур уровня, поэтому моя попытка использовать Data :: Walk.

Что я действительно хочу сделать во время обхода структуры, так это ссылаться на ключ, который используется. Вроде как Data :: Dumper, но с разделением табуляцией, а не в формате кода. Я думаю, что вероятные решения (в порядке предпочтения):

  • Некоторые звонки в Data :: Walk, которые я пропустил.
  • Лучший модуль для этой задачи, о котором я не знаю.
  • Быстрый фрагмент кода, который я могу встроить
  • Мой собственный модуль / форк Data :: Walk / Data :: Dumper (большой хмурый взгляд), который добавит эту функциональность.

use strict;
use File::Basename;
use Data::Walk;

my $files;
while (<>) {
        chomp;
        #ls -l output in a file; referencing filename from it (8th column)
        my @line = split(/ /, $_, 8);
        #fileparse exported by File::Basename
        my ($name,$path) = fileparse($line[7]);
        open (my $fh, '<', $path . $name);
        my $sha = Digest::SHA->new('sha1');
        $sha->addfile($fh);
        #finding files by basename, then unique hash, then however many places it is stored.
        #question not why I don't use the hash as the first field.

        #basename    digest    path
        push(@{$files->{$name}->{$sha->hexdigest}}, $path . $name);
}

my @val;
sub walkit {
        $val[$Data::Walk::depth - 1] =  $_;
        if ($Data::Walk::depth == 3) {
                print join("\t", @val), "\n";
        }
}

&walk (\&walkit, %$files);

Gurus

Ответы [ 2 ]

3 голосов
/ 03 октября 2011

Редактировать : вопреки здравому смыслу, я попытаюсь ответить на этот вопрос снова.

Вот простой подход к печати того, что вы хотите. Использование Data :: Walk неосуществимо, потому что у вас нет ключевого контекста, когда вы находитесь внутри хеша (вы просто получаете указатель на контейнер.)

Эта функция работает для довольно сложных структур. Конечно, это не даст правильного вывода, если вы поместите туда ссылку на функцию или что-то непонятное.

use strict;
use warnings;

my $res;
sub walk {
    my ($item, $path) = @_;
    if (ref $item eq 'ARRAY') {
        foreach (@$item) {
            walk($_, $path);
        }
    } elsif (ref $item eq 'HASH') {
        foreach (keys %$item) {
            push @$path, $_;
            walk($item->{$_}, $path);
            pop @$path;
        }
    } else {
        print join('-', @$path, $item), "\n";
    }
}

my $struct = {
    a => {
            a1 => { a11 => [ 1, 2, 3 ] },
            a2 => { a22 => [5, 6, 7] }
    },
    b => { b1 => [ 99 ], },
    c => [ 100, 101, ],
    d => [ 101, { d2 => { d3 => [200, 210] }, }, ],
};

walk $struct;
3 голосов
/ 03 октября 2011
for my $name (keys(%$files)) {
   for my $digest (keys(%{$files->{$name}})) {
      my @qfns = @{ $files->{$name}{$digest} };
      if (@qfns > 1) {
         say "For $name and $digest,";
         say "   $_" for @qfns;
      }
   }
}

(Я предполагаю, что вы ищете дубликаты, поэтому я ничего не печатаю, когда есть только один путь, связанный с комбинированным именем-дайджестом. Вы можете удалить if, если хотите напечатать все.)

Некоторая другая очистка:

use strict;
use warnings;
use 5.010;

use Digest::SHA    qw( );
use File::Basename qw( basename );

sub calc_digest {
   my ($qfn) = @_;
   open(my $fh, '<', $qfn) or die $!;
   my $sha = Digest::SHA->new('sha1');
   $sha->addfile($fh);
   return $sha->hexdigest();
}

my $files;
while (<>) {
   my $qfn = (split)[7];
   my $name = basename($path);
   my $digest = calc_digest($qfn);
   push @{ $files->{$name}{$digest} }, $qfn;
}

(«qfn» означает «полное имя файла», что означает путь к файлу, который не был тем, что содержал $path. Вы неоднократно создавали путь, даже если $line[7] содержал его.)

...