通过正则表达式进行不区分大小写的单词搜索

时间:2013-02-10 17:47:41

标签: regex string perl search

我刚开始使用perl中的正则表达式。在浏览各种在线教程之后,我想要编写一个正则表达式来匹配命令指定的不区分大小写的单词匹配。

我正在尝试确定字符串“A”是否由字符串“B”的单词或单词序列组成,我想不区分大小写这样做。

例如,如果字符串“B”是“John Von Neumann”,则“JOhn”,“Von NeumaNn”,“VoN”,“john neuMann”将是匹配,但字符串比如“Joh”,“NeumaNn VoN”,“Vonn”会成为一个匹配。

我不知道如何使用正则表达式做任何想法?

2 个答案:

答案 0 :(得分:9)

让我们忽略一下。

John Von Neumann

可以匹配

John Von Neumann    1 1 1
John Von            1 1 0
John     Neumann    1 0 1
John                1 0 0
     Von Neumann    0 1 1
     Von            0 1 0
         Neumann    0 0 1

所以你正在寻找的正则表达式是

/^(?:John Von Neumann|John Von|John Newmann|John|...)\z/i

以下是构建列表的方法:

sub true_indexes {
   my ($n) = @_;
   my $i = 0;
   my @indexes;
   while ($n) {
      push @indexes, $i if $n & 1;
      ++$i;
      $n >>= 1;
   }
   return @indexes;
}

my @words = split(' ', 'John Von Neumann');

my @patterns;
unshift @patterns, join ' ', @words[ true_indexes($_) ]
   for 1 .. (2**@words)-1;

最后,我们可以生成模式:

my $pat = join '|', map quotemeta, @patterns;
my $re = qr/$pat/i;

你会这样使用它:

if ($input =~ /^$re\z/) {
   print "match\n";
} else {
   print "no match\n";
}

答案 1 :(得分:3)

ikegami的解决方案将占用指数空间,以便在将字符串转换为正则表达式之前存储字符串(每个字将出现2 n - 1 次,其中n是字数,所以总空间至少为2 n - 1 *总和(单词长度))。这是与正则表达式引擎相关的 - 因为问题出在字符串转换为正则表达式之前。


与ikegami的解决方案相同的正则表达式构造等效(根据它匹配的字符串集合)将是:

^(?=[^ ])(?:(?: |^)John(?= |\z))?+(?:(?: |^)Von(?= |\z))?+(?:(?: |^)Neumann(?= |\z))?+\z

这只占用线性空间,以字数和所有单词的总长度为准。

为清楚起见:

^
(?=[^ ])
(?:(?: |^)John(?= |\z))?+
(?:(?: |^)Von(?= |\z))?+
(?:(?: |^)Neumann(?= |\z))?+
\z

前瞻断言(?=[^ ])有两个目的:防止空字符串匹配,并确保第一个字符不是空格字符。

请注意?+,它使量词占有(或原子),因为我们不需要在这里进行回溯。 为什么? 如果我们要正常执行此操作,我们将遍历单词列表并将其与输入中最左侧的单词进行比较。找到匹配后,我们将继续循环,将其与输入中的下一个单词进行比较,直到找到输入中的所有单词或者我们已完成循环单词列表。

占有量词也可以阻止地狱的回溯发生。如果某个单词被视为匹配,则永远不会再次重新考虑。

对于每个单词,它们可以以空格开头,或者它是字符串的开头。前瞻断言(?= |\z)的目的是确保具有相同前缀的单词在第一次尝试时不会被错误地匹配(例如"John Von Vone",尝试匹配"John Vone")。< / p>

由于没有回溯,最坏情况下的表现在所有单词的长度和输入字符串的长度方面都是线性的(与没有正则表达式的情况相同)。


我们可以稍微更改正则表达式以允许灵活的间距:

^(?= *+[^ ])(?: *+John(?= |\z))?+(?: *+Von(?= |\z))?+(?: *+Neumann(?= |\z))?+ *+\z

为清楚起见(领先空间很重要):

^
(?= *+[^ ])
(?: *+John(?= |\z))?+
(?: *+Von(?= |\z))?+
(?: *+Neumann(?= |\z))?+
 *+
\z

开头的前瞻(?= *+[^ ])确保输入字符串不包含空格。

正则表达式被更改为允许任何数量的空格位于单词之前(由占有量词不允许回溯)。使用0或更多量词*,对于该字在字符串开头的正确的情况。由于前瞻断言(?= |\z),2个单词不可能发生碰撞。

在构造字符串时(在将其输入到正则表达式引擎之前),它仍占用线性空间。最糟糕的表现也是线性的。


极端情况

  1. 原话:

    aaaaaaaaaaaaaaaaaaa0 aaaaaaaaaaaaaaaaaaa1 ... aaaaaaaaaaaaaaaaaaa9 aaaaaaaaaaaaaaaaaaaa ... aaaaaaaaaaaaaaaaaaaz aaaaaaaaaaaaaaaaaaaA ... aaaaaaaaaaaaaaaaaaaZ
    

    (每个单词为20个字符,最后一个字符从0-9更改,然后是a-z,然后是A-Z

    要搜索的字符串(不匹配):

    aaaaaaaaaaaaaaaaaaaz aaaaaaaaaaaaaaaaaaay
    

    y只能在z

  2. 之前出现
  3. 原字:

    patterns used in Perl pattern matching evolved from those supplied
    

    (一些正常的话)

    要搜索的字符串(不匹配):

    patterns used in Perl pattern matching evolved from those suppliedd
    

    (最后加d

  4. 原字:

    aaaaaaaaaaaa aaaaaaaaaaa aaaaaaaaaa aaaaaaaaa aaaaaaaa aaaaaaa aaaaaa aaaaa aaaa
    

    (Word仅包含a,长度不同。)

    要搜索的字符串(不匹配):

    aaaaaaaaaaaa aaaaaaaaaaa aaaaaaaaaa aaaaaaaaa aaaaaaaa aaaaaaa aaaaaa aaaaa aaaaa
    

    (最后加a

  5. 原字:

    performance abc xyz performance 456 !@# performance
    

    (同一个词出现多次)

    要搜索的字符串(不匹配):

    performance performance performance performance