Простой способ сравнить символ в позиции в строке с остальными строками в той же позиции (Perl) - PullRequest
3 голосов
/ 17 января 2012

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

abcdefg
avcddeg
acbdeeg

Файл будет читать a, d, g как совпадения и возвращать позицию.

Я думал об использовании 2D-массива в Perl для обхода и сравнения всего файла, но это может оказаться утомительным. У кого-нибудь есть более простой способ сделать это?

Спасибо

Ответы [ 5 ]

7 голосов
/ 17 января 2012

Вот умное (и быстрое) решение с использованием побитовых операций.Он основан на том факте, что a & b & ... & z равно a | b | ... | z тогда и только тогда, когда все a, b, ..., z равны.

# read first line:
chomp( $_ = <> );
my $join = my $meet = $_;

# read other lines:
while( <> ) {
    chomp;
    $join |= $_;
    $meet &= $_;
}

# print matching columns:
foreach my $i ( 0 .. length($meet) - 1 ) {
    my $a = substr $join, $i, 1;
    my $b = substr $meet, $i, 1;
    print "$i: $a\n" if $a eq $b;
}

Тестовый вход:

abcdefg
avcddeg
acbdeeg

Выход:

0: a
3: d
6: g

Ps.Это решение работает, даже если линии имеют разную длину;никакие столбцы после конца самой короткой строки не будут считаться совпадающими.

1 голос
/ 17 января 2012

Неэффективно и требует много памяти, но довольно читабельно и просто:

use strict;use warnings;

my $lead = <DATA>;
chomp $lead;
my $rest = do { local $/; <DATA> }; 

for (my $i = 0; $i < length $lead; $i++ ) {
    my $char = substr $lead, $i, 1; 
    next if $rest =~ /^.{$i}[^\Q$char\E]/m;
    print "$i:$char\n";
}


__DATA__
abcdefg
avcddeg
acbdeeg
1 голос
/ 17 января 2012

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

my @matchedIndexes;
my $pattern = "abcdefg";
INDEX:
for $index ( 0 .. ( length($pattern) - 1 ) ){
    for $line (@remainingLines){
        #if we find a nonmatch at the index, cut out.
        if ( !(substr($line, $index, 1) == substr($pattern, $index, 1) ){
            next INDEX;
        }
    }
    #if we made it here without cutting out, the whole set of lines matched.
    push @matchedIndexes, $index;
}
1 голос
/ 17 января 2012

Вы можете использовать побитовое xor ^.Ксоринг двух строк оставляет нули в позициях, где строки идентичны.

use warnings;
use strict;

my $previous;
my $first = 1;
while (<>) {
    chomp;
    $previous = $_ if $first;
    undef $first;
    my $in = $previous ^ $_;
    my $p;
    my @u = unpack 'c*', $in;
    $p .= $u[$_] ? ' ' : substr $previous, $_, 1 for 0 .. $#u;
    $previous = $p;
    last if $p =~ /^ +$/; # no more matches possible
}

print pos $previous, ": $1\n" while $_ = $previous =~ /(\S)/g;
0 голосов
/ 17 января 2012

Вы также можете читать файл построчно, помечая элементы массива как undef, когда есть строка, для которой нет общего соответствия:

use strict;
use warnings;

open(my $read,"<","input_file") or die $!;

my $first=1; #Flag to indicate whether or not we are on the first line.
my @characters=(); #Array for characters

while(my $line=<$read>)
{
  chomp($line);
  if($first)
  {
    @characters=split(//,$line);
    $first=0;
  }
  else
  {
    my @temp_arr=split(//,$line);

    foreach(0..$#characters)
    {
      $characters[$_]=undef unless $characters[$_] eq $temp_arr[$_];
    }
  }

  #If we do not have any characters in common, bail out!
  unless(scalar(grep{defined($_)}@characters))
  {
    print "Sorry, there are no characters in common positions within all rows of file input_file\n";
    exit(1);
  }
}

close($read);

print "Here are the common characters and positions:\n\n";

foreach(0..$#characters)
{
  print "" . ($_ + 1) . ": " . $characters[$_] . "\n" if defined($characters[$_]);
}

Для ввода в вашем вопросе вывод:

Here are the common characters and positions:

1: a
4: d
7: g

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

...