Perl正则表达式|如何从文件中排除单词

时间:2019-04-17 13:28:38

标签: regex perl authentication keycloak

我正在寻找有关项目中某些要求的Perl正则表达式语法。 首先,我想从txt文件(字典)中排除字符串。

例如,如果我的文件包含以下字符串:

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

i使用正则表达式

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

我还有一个无法解决的要求。我必须创建一个正则表达式,不允许字符串具有超过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 个答案:

答案 0 :(得分:3)

从给定列表中排除包含单词的字符串的一种方法是形成带有单词交替形式的模式,并在正则表达式中使用该模式,从而匹配项将字符串排除在外。

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”),然后使用空格将split转换为单词以用于排除。 quotemeta会转义非单词字符,如果单词中发生任何字符。然后将这些|连接起来,以形成带有正则表达式模式的字符串。 (对于复杂的模式,请使用qr。)

根据您的用例,可能会针对与公共部分交替的模式顺序进行调整和改进。

检查连续重复的字符是否出现三次以上

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

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

由于正则表达式中的aaaa量词,一长串重复的字符(+)被视为一个实例。如果您希望计算所有对,请删除+,然后将四个a视为两对。每次在字符串的不同位置重复的同一字符都会计数,因此aaXaa会计数为两个。

此代码段可以直接添加到上述程序中,该程序将使用文件名和用于排除的单词来调用。他们都打印了所提供样本的预期结果。


考虑一个带有排除词的示例:sosolesolely。如果您只需要检查这些匹配项中的任何一个,那么您需要在替换中先选择较短的匹配项

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 :(得分:1)

要与文件中的单词不匹配,您可以检查whether a string contains a substring或使用否定的前瞻和交替:

^(?!.*(?:tree|car|ship)).*$
  • ^断言字符串的开头
  • (?!否定前瞻,断言右边的不是
    • .*(?:tree|car|ship)匹配除换行符以外的任何字符,0倍以上,并匹配树车或轮船
  • )近距离否定
  • .*匹配除换行符以外的所有字符
  • $声明字符串结尾

Regex demo

要使字符串不超过3次重复字符,可以使用:

\b(?!(?:\w*(\w)\1){3})\w+\b
  • \b字边界
  • (?!负向前进,断言右边的不是
    • (?: NOn捕获组
    • \w*(\w)\1匹配0+次单词字符,然后在组中捕获单词char,然后使用\1对该组进行反向引用
    • ){3}关闭非捕获组并重复3次
  • )近距离否定预测
  • \w+匹配1个以上的单词字符
  • \b单词边界

Regex demo

更新

根据this posted answer(您可能会添加到问题中),您有两种要组合的模式,但不起作用:

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

在这2个模式中,您使用2个捕获组,因此第二个模式必须指向第二个捕获组\2

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

Pattern demo

答案 2 :(得分:0)

我希望其他人会提供更好的解决方案,但这似乎可以满足您的要求:

\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 regex还具有\w的简写形式,它与[A-Za-z0-9_]等效,如果要匹配单词中的任何字符,可以很方便。

答案 3 :(得分:0)

我的问题是我有2个可正常工作的正则表达式:

不允许超过3对字符:

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

不允许字符重复超过4次:

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

现在我想将它们合并为一行,例如:

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

但是它只工作在最前面的正则表达式,而不是两者都起作用

答案 4 :(得分:0)

如对@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;
...

HTH