Проверка, присутствует ли часть строки в ключах хеш-таблицы

#perl #hash

#perl #хэш

Вопрос:

Я имею дело с хэш-таблицей в perl.

У меня есть несколько строк с несколькими длинами и несколькими - :

 pre1-pre2-text1-text2
pre3-text3
pre4-pre5-pre6-text4
 

У меня есть a %hash со следующими ключами:

 pre1-pre2
pre3
pre4-pre5-pre6
 

Таким образом, ключи %hash содержат только pre часть строк.

Как я могу проверить, есть ли совпадение, скажем, между первой строкой pre1-pre2-text1-text2 и ключами %hash ?

Ответ №1:

Один из способов: сформировать шаблон, используя чередование ключей, и протестировать строки по нему

 use warnings;
use strict;
use feature 'say';

my @strings = qw(pre-not pre1-pre2-text1-text2 pre3-text3 pre4-pre5-pre6-text4);

my %h = ( 'pre1-pre2' => 1, 'pre3' => 1, 'pre4-pre5-pre6' => 1 );

my $keys_re = join '|', map { quotemeta } keys %h; 

foreach my $str (@strings) { 
    say $str  if $str =~ /$keys_re/;
}
 

Это имеет квадратичную сложность, но чередование не будет проходить через все ключи, и это C (само регулярное выражение).

Возможное улучшение (или необходимость!) может быть, для подходящей сортировки ключей. Например, кратчайший первый

 my $keys_re = join '|', map { quotemeta } sort { length $a <=> length $b } keys %h; 
 

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

Чтобы также получить сам ключ, добавьте захватывающую скобку вокруг шаблона

 foreach my $str (@strings) { 
    say "$str matched by key: $1"  if $str =~ /($keys_re)/;
}
 

где $1 содержит совпадающее и захваченное чередование, которое является ключом.

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

1. Спасибо. Условие if оценивается как true, так что это кажется правильным. Однако, как я могу теперь распечатать совпадение и получить доступ к значению, связанному с этим совпадением, в моем исходном%h?

2. Добро пожаловать @user1987607. Для этого вы хотите зафиксировать совпадение. Это хороший момент, он должен быть в сообщении — добавлено примечание.

Ответ №2:

Я добавил входные данные, предоставленные вами в небольшом коде perl, и я могу проверить, есть ли совпадение в ключах

 #!/usr/bin/perl
use warnings;

my %langs = ( "pre1-pre2" => 'pre1-pre2',
 "pre3" => 'pre3',
 "pre4-pre5-pre6" => 'pre4-pre5-pre6');

@pats=("pre1-pre2-text1-text2", "pre3-text3", "pre4-pre5-pre6-text4");

for(keys %langs){
  foreach $ss (@pats){
    if (index($ss,$_) != -1){
      print("Key contains:",$_, "|", $ss,"n");
    }
    else{
      print("NOT FOUND:",$_, "|", $ss,"n");
    }
  }
}
 

ПРИМЕЧАНИЕ: Если я правильно понял ваше требование, это вам поможет.

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

1. Здесь несколько интересных вариантов кодирования. Почему use warnings , но нет use strict ? Почему объявляется %langs с my помощью, но без других переменных? Зачем переключаться между for и foreach ? Зачем передавать несколько аргументов print() вместо интерполяции переменных в одну строку? Зачем использовать print() , нет say() ?

2. И если это «правильный» ответ, которым он вполне может быть, то хэши на самом деле ничего вам не дают. Если вы не можете извлечь ключ для поиска O (1), то в хэше мало смысла (по крайней мере, в этом случае), и вы можете также использовать массив.

3. @DaveCross. Прошло 8 лет, я работал в Perl, и поэтому я искал примеры и создал свой пример. Так что будут некоторые проблемы 🙂

4. @Raghuram: Это проблема с объединением кода из разных источников. В одном и том же примере вы можете получить совершенно разные стили, и это может легко запутать новичка. Поставьте себя на место новичка, читающего ваш код. Я бы подумал: «итак, вы используете for для перебора хэша и foreach перебора массива» — и это просто неправильно.

Ответ №3:

Этот ответ предполагает, что pre это не может произойти в середине строки (т. Е. У вас не будет строки, подобной pre1-pre2-text1-pre5 тому, где будет только ваш префикс pre1-pre2 ). Если это предположение неверно, используйте /^((?:pred )(?:-pred )*)/ вместо /^(.*pred )/ (я предпочитаю последнее, потому что оно более читабельно, но первое более точное).

 #!/usr/bin/perl

use strict;
use warnings;
use feature 'say';

my %pre = map { $_ => 1 } qw(pre1-pre2 pre3 pre4-pre5-pre6);

while (<DATA>) {
    my ($prefix) = /^(.*pred )/;
    if ($prefix amp;amp; exists $pre{$prefix}) {
        say "Prefix exists: $prefix";
    } else {
        say "Prefix doesn't exist: $prefix";
    }
}

__DATA__
pre1-pre2-text1-text2
pre3-text3
pre4-pre5-pre6-text4
pre7-pre8-text5
 

Если у вас может быть строка pre1-pre2-text1 , где префикс должен быть просто pre1 , то это решение не будет работать. В этом случае у вас не будет другого выбора, кроме как перебрать все ключи хэша и проверить, соответствуют ли они началу строки:

 while (<DATA>) {
    for my $prefix (keys %pre) {
        if (/^Q$prefix/) {
            say "Found prefix: $prefix";
            last;
        }
    }
}
 

Однако это гораздо менее эффективно, поскольку вам нужно перебирать все хэш-ключи для каждой строки.
Относительно Q : это гарантирует, что это решение работает, даже если ваши префиксы содержат специальные символы регулярных выражений (например , или . ). Если ваши префиксы всегда одинаковы pre1-pre2 , тогда вы можете опустить Q .


Если у вас возникли проблемы с пониманием my %pre = map { $_ => 1 } qw(pre1-pre2 pre3 pre4-pre5-pre6); : это краткая версия

 my %prev = (
    'pre1-pre2'      => 1,
    'pre3'           => 1,
    'pre4-pre5-pre6' => 1
);