Быстрый способ найти разницу между двумя строками одинаковой длины в Perl - PullRequest
5 голосов
/ 17 января 2011

Приведенные пары строк, как это.

    my $s1 = "ACTGGA";
    my $s2 = "AGTG-A";

   # Note the string can be longer than this.

Я хотел бы найти позицию и символ в $s1, где он отличается от $s2.В этом случае ответ будет:

#String Position 0-based
# First col = Base in S1
# Second col = Base in S2
# Third col = Position in S1 where they differ
C G 1
G - 4

Я могу легко добиться этого с помощью substr().Но это ужасно медленно.Обычно мне нужно сравнить миллионы таких пар.

Есть ли быстрый способ добиться этого?

Ответы [ 4 ]

22 голосов
/ 17 января 2011

Stringwise ^ ваш друг:

use strict;
use warnings;
my $s1 = "ACTGGA";
my $s2 = "AGTG-A";

my $mask = $s1 ^ $s2;
while ($mask =~ /[^\0]/g) {
    print substr($s1,$-[0],1), ' ', substr($s2,$-[0],1), ' ', $-[0], "\n";
}

ПОЯСНЕНИЯ:

Оператор ^ (исключающий или), когда используется со строками, возвращает строку, состоящую из результата исключительного или каждого бита числового значения каждого символа. Разбиваем пример на эквивалентный код:

"AB" ^ "ab"
( "A" ^ "a" ) . ( "B" ^ "b" )
chr( ord("A") ^ ord("a") ) . chr( ord("B") ^ ord("b") )
chr( 65 ^ 97 ) . chr( 66 ^ 98 )
chr(32) . chr(32)
" " . " "
"  "

Полезная особенность этого здесь в том, что нулевой символ ("\0") возникает тогда и только тогда, когда две строки имеют одинаковый символ в данной позиции. Таким образом, ^ может использоваться для эффективного сравнения каждого символа двух строк в одной быстрой операции, и в результате можно искать ненулевые символы (что указывает на разницу). Поиск можно повторить, используя флаг регулярного выражения / g в скалярном контексте, а положение каждой разности символов можно найти, используя $-[0], что дает смещение начала последнего успешного соответствия.

4 голосов
/ 17 января 2011

Использовать двоичные битовые операции в полных строках.

Такие вещи, как $s1 & $s2 или $s1 ^ $s2, работают невероятно быстро и работают со строками произвольной длины.

3 голосов
/ 22 ноября 2012

Мне было скучно на Дне благодарения 2012 года, и я ответил на вопрос и многое другое. Он будет работать на строках одинаковой длины. Это будет работать, если они не. Я добавил помощь, выбирая обработку просто для удовольствия. Я думал, что кто-то может найти это полезным. Если вы новичок в PERL добавить не знаю. Не добавляйте в программу код, указанный ниже DATA . Повеселись.

. / Diftxt -h

    usage: diftxt [-v ] string1 string2
                   -v = Verbose 
                  diftxt [-V|--version]
                  diftxt [-h|--help]  "This help!"
Examples:  diftxt test text
           diftxt "This is a test" "this is real"

    Place Holders:  space = "·" , no charater = "ζ"

cat ./diftxt ----------- вырезать ✂ ----------

#!/usr/bin/perl -w

use strict;
use warnings;
use Getopt::Std;
my %options=();
getopts("Vhv", \%options);
my $helptxt='
        usage: diftxt [-v ] string1 string2
                       -v = Verbose 
                      diftxt [-V|--version]
                      diftxt [-h|--help]  "This help!"
    Examples:  diftxt test text
               diftxt "This is a test" "this is real"

        Place Holders:  space = "·" , no charater = "ζ"';
my $Version = "inital-release 1.0 - Quincey Craig 11/21/2012";

print "$helptxt\n\n" if defined $options{h};
print "$Version\n" if defined $options{V};
if (@ARGV == 0 ) {
 if (not defined $options{h}) {usage()};
 exit;
}

my $s1 = "$ARGV[0]";
my $s2 = "$ARGV[1]";
my $mask = $s1 ^ $s2;

#  setup unicode output to STDOUT
binmode DATA, ":utf8";
my $ustring = <DATA>;
binmode STDOUT, ":utf8";

my $_DIFF = '';
my $_CHAR1 = '';
my $_CHAR2 = '';

sub usage
{
        print "\n";
        print "usage: diftxt [-v ] string1 string2\n";
        print "               -v = Verbose \n";
        print "       diftxt [-V|--version]\n";
        print "       diftxt [-h|--help]\n\n";
        exit;
}

sub main
{
 print "\nOrig\tDiff\tPos\n----\t----\t----\n" if defined $options{v};
 while ($mask =~ /[^\0]/g) {
### redirect stderr to allow for test of empty variable with error message from substr   
    open STDERR, '>/dev/null';
    if (substr($s2,$-[0],1) eq "") {$_CHAR2 = "\x{03B6}";close STDERR;} else {$_CHAR2 = substr($s2,$-[0],1)};
    if (substr($s2,$-[0],1) eq " ") {$_CHAR2 = "\x{00B7}"};
      $_CHAR1 = substr($s1,$-[0],1);
    if ($_CHAR1 eq "") {$_CHAR1 = "\x{03B6}"} else {$_CHAR1 = substr($s1,$-[0],1)};
    if ($_CHAR1 eq " ") {$_CHAR1 = "\x{00B7}"};
### Print verbose Data  
   print $_CHAR1, "\t", $_CHAR2, "\t", $+[0], "\n" if defined $options{v};
### Build difference list 
   $_DIFF = "$_DIFF$_CHAR2";
### Build mask 
   substr($s1,"$-[0]",1) = "\x{00B7}";
 } ### end loop

 print "\n" if defined $options{v};
 print "$_DIFF, ";
 print "Mask: \"$s1\"\n";
} ### end main
if ($#ARGV == 1) {main()};
__DATA__
0 голосов
/ 17 января 2011

Это самая простая форма, которую вы можете получить

my $s1 = "ACTGGA";
my $s2 = "AGTG-A";

my @s1 = split //,$s1;
my @s2 = split //,$s2;

my $i = 0;
foreach  (@s1) {
    if ($_ ne $s2[$i]) {
        print "$_, $s2[$i] $i\n";
    }
    $i++;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...