Perl Регулярное выражение | как исключить слова из файла - PullRequest
2 голосов
/ 17 апреля 2019

Я искал синтаксис регулярных выражений Perl о некоторых требованиях, которые у меня есть в проекте.Сначала я хочу исключить строки из текстового файла (словаря).

Например, если в моем файле есть следующие строки:

path.../Document.txt |
  tree
  car
  ship

я использую регулярное выражение

a1testtre  --  match
orangesh1  --  match
apleship3  --  not match  [contains word from file ]

Также у меня есть еще одно требование, которое я не могу решитьЯ должен создать регулярное выражение, которое не позволяет String иметь более чем 3 раза повторение символа (два символа).

Например:

adminnisstrator21     -- match  (have 2 times a repetition of chars)
kkeeykloakk           -- not match have over 3 times repetition
stack22ooverflow      -- match  (have 2 times a repetition of chars)

для этого у меня есть попытка

\b(?:([a-z])(?!\1))+\b

но это работает только для первого повторения символа. Есть идеи, как решить эти два?

Ответы [ 5 ]

3 голосов
/ 17 апреля 2019

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

use warnings;
use strict;
use feature qw(say);

use Path::Tiny;

my $file = shift // die "Usage: $0 file\n";  #/

my @words = split ' ', path($file)->slurp;

my $exclude = join '|', map { quotemeta } @words;

foreach my $string (qw(a1testtre orangesh1 apleship3)) 
{ 
    if ($string !~ /$exclude/) { 
        say "OK: $string"; 
    }
}

Я использую Path :: Tiny , чтобы прочитать файл в строку ("slurp"), которая затем разделяется на пробелы в слова, которые используются для исключения. quotemeta экранирует не-словесные символы, если таковые встречаются в ваших словах, которые затем соединяются |, чтобы сформировать строку с шаблоном регулярных выражений. (Для сложных паттернов используйте qr .)

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

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

foreach my $string (qw(adminnisstrator21 kkeeykloakk stack22ooverflow))
{
    my @chars_that_repeat = $string =~ /(.)\1+/g;

    if (@chars_that_repeat < 3) { 
        say "OK: $string";
    }
}

Длинная строка повторяющихся символов (aaaa) считается одним экземпляром из-за квантификатора + в регулярном выражении; если вы предпочитаете считать все пары, удалите +, и четыре a s будут считаться двумя парами. Один и тот же символ, повторяющийся в разных местах строки, считается каждый раз, поэтому aaXaa считается двумя парами.

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


& кинжалом; & thinsp; Рассмотрим пример со словами исключения: so, sole и solely. Если вам нужно только проверить, совпадает ли какое-либо из этих совпадений, то сначала вам понадобятся более короткие в чередовании

my $exclude = join '|', map { quotemeta } sort { length $a <=> length $b } @words;
#==>  so|sole|solely

для более быстрого совпадения (so соответствует всем трем). Это, безусловно, имеет место здесь.

Но, если вы хотите правильно определить, какое слово соответствует, то у вас должно быть сначала длинные слова,

solely|sole|so

, чтобы строка solely правильно соответствовала своему слову, прежде чем она может быть "украдена" so. Тогда в этом случае вы захотите наоборот sort { length $b <=> length $a }

1 голос
/ 17 апреля 2019

Чтобы не совпадать слово из файла, вы можете проверить , содержит ли строка подстроку или использовать отрицательный прогноз и чередование:

^(?!.*(?:tree|car|ship)).*$
  • ^ Подтвердить начало строки
  • (?! негативный взгляд, утверждай, что справа нет
    • .*(?:tree|car|ship) Совпадение 0+ раз с любым символом, кроме новой строки, и совпадение с деревом или кораблем
  • ) Закрыть негативный взгляд
  • .* Соответствует любому символу, кроме новой строки
  • $ Утверждение конца строки

Regex demo

Чтобы строка не повторялась более 3 раз, вы можете использовать:

\b(?!(?:\w*(\w)\1){3})\w+\b
  • \b Граница слова
  • (?! Отрицательный взгляд, утверждай, что справа нет
    • (?: Нет, группа захвата
    • \w*(\w)\1 Совпадение 0+ раз символа слова с последующим захватом слова char в группе с последующей обратной ссылкой, используя \1 для этой группы
    • ){3} Закрыть группу без захвата и повторить 3 раза
  • ) закрыть негативный прогноз
  • \w+ Совпадение 1+ символов слова
  • \b граница слова

Regex demo

Обновление

Согласно этому опубликованному ответу (который вы могли бы вместо этого добавить к вопросу) у вас есть 2 шаблона, которые вы хотите объединить, но они не работают:

(?=^(?!(?:\w*(.)\1){3}).+$)(?=^(?:(.)(?!(?:.*?\1){4}))*$)

В этих 2 шаблонах вы используете 2 группы захвата, поэтому второй шаблон должен указывать на вторую группу захвата \2.

(?=^(?!(?:\w*(.)\1){3}).+$)(?=^(?:(.)(?!(?:.*?\2){4}))*$)
                                               ^  

Демоверсия шаблона

0 голосов
/ 25 апреля 2019

Как уже упоминалось в комментарии к ответу @ zdim, сделайте это немного дальше, убедившись, что порядок, в котором ваши слова собраны в шаблон совпадения, вас не смущает. Если слова в файле не очень тщательно упорядочены, чтобы начать, я использую подпрограмму, подобную этой, при построении строки соответствия:

# Returns a list of alternative match patterns in tight matching order.
# E.g., TRUSTEES before TRUSTEE before TRUST   
# TRUSTEES|TRUSTEE|TRUST

sub tight_match_order {
    return @_ unless @_ > 1;
    my (@alts, @ordered_alts, %alts_seen);
    @alts   = map { $alts_seen{$_}++ ? () : $_ } @_;
    TEST: {
        my $alt = shift @alts;
        if (grep m#$alt#, @alts) {
            push @alts => $alt;
        } else {
            push @ordered_alts => $alt;
        }
        redo TEST if @alts;
    }
    @ordered_alts
}

Итак, следуя ответу @ zdim:

...
my @words = split ' ', path($file)->slurp;

@words = tight_match_order(@words); # add this line

my $exclude = join '|', map { quotemeta } @words;
...

НТН

0 голосов
/ 19 апреля 2019

Моя проблема в том, что у меня есть 2 регулярных выражения, которые работают:

Не разрешать более 3 пар символов:

          (?=^(?!(?:\w*(.)\1){3}).+$)

Не разрешать повторять символ более 4 раз:

        (?=^(?:(.)(?!(?:.*?\1){4}))*$)

Теперь я хочу объединить их в один ряд, например:

      (?=^(?!(?:\w*(.)\1){3}).+$)(?=^(?:(.)(?!(?:.*?\1){4}))*$)

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

0 голосов
/ 17 апреля 2019

Я надеюсь, что кто-то еще придет с лучшим решением, но, похоже, это делает то, что вы хотите:

\b                          Match word boundary
  (?:                       Start capture group
    (?:([a-z0-9])(?!\1))*   Match all characters until it encounters a double
    (?:([a-z0-9])\2)+       Match all repeated characters until a different one is reached
  ){0,2}                    Match capture group 0 or 2 times
  (?:([a-z0-9])(?!\3))+     Match all characters until it encounters a double
\b                          Match end of word

Я изменил [a-z], чтобы также соответствовать числам, поскольку приведенные вами примеры, кажется,также включить номера.В регулярном выражении Perl также есть сокращение \w, что эквивалентно [A-Za-z0-9_], что может пригодиться, если вы хотите сопоставить любой символ в слове.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...