Регулярные выражения, оператор сопоставления с использованием строковой переменной в Perl

#regex #perl #string-matching

#регулярное выражение #perl #сопоставление строк

Вопрос:

Я использую a regex , но получаю несколько странных, неожиданных «совпадений». «Имена» отправляются в подпрограмму для сравнения с вызываемым массивом @ASlist , который содержит несколько строк. Первый элемент каждой строки также является именем, за которым следует от 0 до нескольких синонимов. Цель состоит в том, чтобы сопоставить входящее «имя» с любой строкой, в @ASlist которой есть соответствующая ячейка.

Пример ввода, из которого $names извлекается для сравнения с @ASlist :

 13  1   13  chr7    7   70606019    74345818    Otud7a  Klf13   E030018B13Rik   Trpm1   Mir211  Mtmr10  Fan1    Mphosph10   Mcee    Apba2   Fam189a1    Ndnl2   Tjp1    Tarsl2  Tm2d3   1810008I18Rik   Pcsk6   Snrpa1  H47 Chsy1   Lrrk1   Aldh1a3 Asb7    Lins    Lass3   Adamts17
  

Примеры строк из @asList:

 HSPA5   BIP FLJ26106    GRP78   MIF2        
NDUFA5  B13 CI-13KD-B   DKFZp781K1356   FLJ12147    NUFM    UQOR13
ACAN    AGC1    AGCAN   CSPG1   CSPGCP  MSK16   SEDK
  

Код:

 my ($name) = @_;  ## this comes in from another loop elsewhere in code I did not include
chomp $name;

my @collectmatches = (); ## container to collect matches

foreach my $ASline ( @ASlist ){

    my @synonyms = split("t", $ASline );

    for ( my $i = 0; $i < scalar @synonyms; $i   ){
         chomp $synonyms[ $i ];
         #print "COMPARE $name TO $synonyms[ $i ]n";

         if ( $name =~m/$synonyms[$i]/ ){
              print "tname $name from block matchesnt$synonyms[0]ntvia $synonyms[$i] from AS listn";
              push ( @collectmatches, $synonyms[0], $synonyms[$i] ); 
          }
         else {
              # print "$name does not match $synonyms[$i]n"; 
         }
    }
}
  

Скрипт работает, но также сообщает о странных совпадениях. Например, когда $name «E030018B13Rik» соответствует «NDUFA5», когда он встречается в @ASlist . Эти два не должны совпадать.

Если я изменю регулярное выражение с ~m/$synonyms[$i]/ на ~m/^$synonyms[$i]$/ , «странные» совпадения исчезнут, НО сценарий пропустит подавляющее большинство совпадений.

Комментарии:

1. Ваши входные данные выглядят как данные фиксированной ширины. Так ли это? Если это так, регулярное выражение может даже не быть оптимальным инструментом для распаковки данных.

Ответ №1:

NDUFA5 Запись содержит B13 в качестве шаблона, который будет соответствовать E030018<B13>Rik .

Если вы хотите быть более буквальным, добавьте граничные условия в свое регулярное выражение /b...b/ . Также, вероятно, следует избегать использования специальных символов регулярного выражения quotemeta .

 if ( $name =~ m/bQ$synonyms[$i]Eb/ ) {
  

Или, если вы хотите проверить прямое равенство, просто используйте eq

 if ( $name eq $synonyms[$i] ) {
  

Комментарии:

1. Я не понимаю, как именно эти два на самом деле совпадают?

2. Потому B13 что находится внутри строки E030018B13Rik . Если вы не хотите, чтобы это выполнялось подобным образом, вам нужно добавить граничные условия к регулярному выражению.

Ответ №2:

Другой, более простой способ проверки на равенство строк — использовать хэш.

Вы не показываете никаких реальных тестовых данных, но эта короткая программа Perl создает хэш из вашего массива @ASlist строк совпадающих строк. После этого большая часть работы выполнена.

Последующий for цикл проверяет E030018B13Rik , является ли он одним из ключей нового %ASlist , и печатает соответствующее сообщение

 use strict;
use warnings;

my @ASlist = (
    'HSPA5   BIP FLJ26106    GRP78   MIF2',
    'NDUFA5  B13 CI-13KD-B   DKFZp781K1356   FLJ12147    NUFM    UQOR13',
    'ACAN    AGC1    AGCAN   CSPG1   CSPGCP  MSK16   SEDK',
);

my %ASlist = map { $_ => 1 } map /S /g, @ASlist;

for (qw/ E030018B13Rik /) {
  printf "%s %sn", $_, $ASlist{$_} ? 'matches' : 'doesn't match';
}
  

вывод

 E030018B13Rik doesn't match
  

Ответ №3:

Поскольку вам нужно сравнить только две строки, вы можете просто использовать eq:

 if ( $name eq $synonyms[$i] ){
  

Ответ №4:

Вы используете B13 в качестве регулярного выражения. Поскольку ни один из символов не имеет специального значения, любая строка, содержащая подстроку B13 , соответствует выражению.

 E030018B13Rik
       ^^^
  

Если вы хотите, чтобы выражение соответствовало всей строке, используйте якоря:

 if ($name =~m/^$synonyms[$i]$/) {
  

Или используйте index или eq для обнаружения подстрок (или идентичных строк соответственно), поскольку ваш ввод, похоже, не использует какие-либо функции регулярных выражений.

Комментарии:

1. Я теряю много потенциальных совпадений, используя эти точные привязки, но я вижу, что могу изменить регулярное выражение, чтобы оно не учитывало регистр…