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

问题描述 投票:2回答:5

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

例如,如果我的文件有这个字符串:

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

我使用正则表达式

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

我还有一个要求,我无法解决。我必须创建一个Regex,它不允许String有3次以上的char重复(两个字符)。

例如 :

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

但它只适用于第一个char-repeat任何想法如何解决这两个?

regex perl authentication keycloak
5个回答
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量词,一长串重复的字符(+)算作一个实例;如果你更愿意计算所有对除去+和四个as将计为两对。每次在字符串中的不同位置重复相同的字符计数,因此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
投票

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

^(?!.*(?:tree|car|ship)).*$
  • ^断言字符串的开头
  • (?!否定前瞻,断言右边的是不是 .*(?:tree|car|ship)匹配任何字符的0次以上,除了换行符并匹配树车或船
  • )关闭否定前瞻
  • .*匹配除换行符之外的任何字符
  • $断言字符串的结尾

Regex demo

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

\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(您可能会添加到问题中),您有2个模式要组合但它不起作用:

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

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

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

Pattern demo


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正则表达式也有\w速记,相当于[A-Za-z0-9_],如果你想匹配单词中的任何字符,这可能很方便。


0
投票

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

不允许超过3对字符:

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

重复不允许超过4次char:

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

现在我想将它们组合成一行,如:

      (?=^(?!(?:\w*(.)\1){3}).+$)(?=^(?:(.)(?!(?:.*?\1){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

© www.soinside.com 2019 - 2024. All rights reserved.