Сравнение двух обработанных ключей в двух хешах - PullRequest
1 голос
/ 23 февраля 2012

Я хочу прочитать в файле с некоторыми символами, такими как "!"и "^" и хотел бы удалить их, прежде чем сравнивать их с другими строками из другой строки.Если после удаления символов обе строки совпадают, я хочу сохранить их в другом хеше, называемом «общий».Например ... FileA:

hello!world
help?!3233
oh no^!!
yes!

FileB:

hello
help?
oh no
yes

В этом случае FileA и FileB должны быть идентичны, так как я сравниваю символы до места, где "!"или появляется «^».Я читаю файлы, используя следующий код:

open FILEA, "< script/".$fileA or die;
my %read_file;
while (my $line=<FILEA>) {
   (my $word1,my $word2) = split /\n/, $line;
   $word1 =~ s/(!.+)|(!.*)|(\^.+)|(\^.*)//;#to remove ! and ^
   $read_file{$word1} = $word1;
}
close(FILEA);

Я распечатал ключи в хэше, и он показывает правильный результат (т. Е. Он преобразует FileA в "привет, помогите?", О нет, даОднако, когда я делаю сравнение FileA и FileB, используя следующий код, он всегда терпит неудачу.

while(($key,$value)=each(%config))
{
    $num=keys(%base_config);
    $num--;#to get the correct index
    while($num>=0)
    {
        $common{$value}=$value if exists $read_file{$key};#stored the correct matches in %common
        $num--;
    }
}

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

use strict;
use warnings;

my $str="hello^vsd";
my $test="hello";
$str =~ s/(!.+)|(!.*)|(\^.+)|(\^.*)//;
my %hash=();
$hash{$str}=();
foreach my $key(keys %hash)
{
    print "$key\n";
}
print "yay\n" if exists $hash{$test};
print "boo\n" unless exists $hash{$test};

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

Ответы [ 4 ]

0 голосов
/ 06 декабря 2012

Сначала мы должны нормализовать ваш вклад.Код ниже создает один хеш для каждого пути.Для каждой строки в данном файле удалите все, начиная с первого символа ! или ^, и запишите его наличие.

sub read_inputs {
  my @result;

  foreach my $path (@_) {
    my $data = {};

    open my $fh, "<", $path or die "$0: open $path: $!";
    while (<$fh>) {
      chomp;
      s/[!^].*//;  # don't put the caret first without escaping!
      ++$data->{$_};
    }

    push @result, $data;
  }

  wantarray ? @result : \@result;
}

Вычисление пересечения двух массивов покрытов разделе Управление данными в списке FAQ Perl .Приспосабливая технику к вашей ситуации, мы хотим знать линии, которые являются общими для всех входов.

sub common {
  my %matches;
  for (@_) {
    ++$matches{$_} for keys %$_;
  }

  my @result = grep $matches{$_} == @_, keys %matches;
  wantarray ? @result : \@result;
}

Связывание его с

my @input = read_inputs "FileA", "FileB";
my @common = common @input;
print "$_\n" for sort @common;

дает вывод

hello
help?
oh no
yes
0 голосов
/ 23 февраля 2012

Вот еще одно решение, которое считывает оба файла одновременно (предполагается, что оба файла имеют одинаковое количество строк):

use strict;
use warnings;

our $INVALID = '!\^'; #regexp character class, must escape

my $fileA = "file1.txt";
my $fileB = "file2.txt";

sub readl
{
  my $fh = shift;
  my $ln = "";

  if ($fh and $ln = <$fh>)
  {
    chomp $ln;
    $ln =~ s/[$INVALID]+.*//g;
  }

  return $ln;
}

my ($fhA, $fhB);
my ($wdA, $wdB);
my %common = ();

open $fhA, $fileA or die "$!\n";
open $fhB, $fileB or die "$!\n";

while ($wdA = readl($fhA) and $wdB = readl($fhB))
{
  $common{$wdA} = undef if $wdA eq $wdB;
}

print "$_\n" foreach keys %common;

Вывод

andrew@gidget:comparefiles$ cat file1.txt 
hello!world
help?!3233
oh no^!!
yes!

andrew@gidget:comparefiles$ cat file2.txt 
hello
help?
oh no
yes

andrew@gidget:comparefiles$ perl comparefiles.pl 
yes
oh no
hello
help?
0 голосов
/ 23 февраля 2012

Начните с упаковки многократно используемых сегментов в подпрограммы:

sub read_file {
    open my $fh, "<", $_[0] or die "read_file($_[0]) error: $!";
      # lexical handles auto-close when they fall out of scope
      # and detailed error messages are good
    my %file;
    while (my $line = <$fh>) {
        chomp $line;          # remove newline
        $line =~ s{[!^].*}{}; # remove everything starting from ! or ^
        $file{$line}++;
    }
    \%file
}

read_file принимает имя входного файла и возвращает хэш сегментов строки перед любыми символами ! или ^. Каждый сегмент строки является ключом, а значение - это число раз, которое оно появилось.

Используя это, следующий шаг должен выяснить, какие строки совпадают между файлами:

my ($fileA, $fileB) = map {read_file $_} your_file_names_here();

my %common;
$$fileA{$_} and $common{$_}++ for keys %$fileB;

print "common: $_\n" for keys %common;

Который напечатает:

common: yes
common: oh no
common: hello
common: help?

Вы можете определить your_file_names_here следующим образом, если хотите протестировать его:

sub your_file_names_here {\(<<'/A', <<'/B')}
hello!world
help?!3233
oh no^!!
yes!
/A
hello
help?
oh no
yes
/B
0 голосов
/ 23 февраля 2012

Вы можете использовать классы символов regex s / [? ^] // g, чтобы удалить ^ и?, Обратите внимание, что ^ должен быть последним в группе, или вам нужно его избежать. (может быть безопаснее избежать этого, если вы добавите других персонажей позже, чтобы они не отрицались).

Я обрабатываю все файлы, используя хэш, чтобы вычислить, для какого файла существует слово.

Для сравнения различий я использую 2 ** (количество файлов), поэтому я получаю значения 2 ** 0 = 1, 2 ** 1 = 2, 2 ** 2 = 4 и так далее. Я использую, чтобы показать, какому файлу принадлежат строки. Если они существуют во всех, они будут равны общему количеству файлов, поэтому 2 в данном случае - 3 (2 + 1) означает, что они находятся в обоих файлах, 1 означает только FileA, 2 означает FileB. Вы проверяете это, выполняя битовые и (&).

Редактировать: добавлены условия теста

<!-- language: perl -->

my @files = qw(FileA.txt FileB.txt);
my %words;
foreach my $i (0 .. $#files) {
    my $file = $files[$i];
    open(FILE,$file) or die "Error: missing file $file\n$!\n";
    while (<FILE>) {
        chomp;
        next if /^$/;
        my ($word) = split /[!\^]/;
        $word =~ s/[?\^]//g; # removes ^ and ?
        $words{$word} += 2**$i;
    }
    close(FILE);
}

my %common;
foreach my $key (sort keys %words) {
    my @found;
    foreach my $i (0 .. $#files) {
        if ( $words{$key} & 2**$i ) { push @found, $files[$i] }
    }
    if ( $words{$key} & 2**$#files ) { $common{$key}++ }
    printf "%10s %d: @found\n",$key,$words{$key};
}

my @tests = qw(hello^vsd chuck help? test marymary^);
print "\nTesting Words: @tests\n";
foreach (@tests) {
    my ($word) = split /[!\^]/;
    $word =~ s/[?\^]//g; # removes ^ and ?
    if ( exists $common{ $word } ) {
        print "Found: $word\n";
    }
    else {
        print "Cannot find: $word\n";
    }
}

Выход:

    bahbah 2: FileB.txt
   chucker 1: FileA.txt
     hello 3: FileA.txt FileB.txt
      help 3: FileA.txt FileB.txt
  marymary 2: FileB.txt
     oh no 3: FileA.txt FileB.txt
      test 1: FileA.txt
       yes 3: FileA.txt FileB.txt

Testing Words: hello^vsd chuck help? test marymary^
Found: hello
Cannot find: chuck
Found: help
Cannot find: test
Found: marymary
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...