【问题标题】:Counting presence of words within context (near other words)计算上下文中单词的存在(靠近其他单词)
【发布时间】:2015-09-18 01:14:08
【问题描述】:

首先,感谢您在这个问题上给我的任何帮助。我有一个单词列表,在下面的示例中它是一个颜色列表。我们称之为 WORD_LIST_1。 我想计算每个单词出现在正文中的次数。我可以用一个简单的正则表达式来做到这一点。 但是,我有另一个捕获上下文的单词列表。在下面的示例中,上下文是宠物列表。我们称之为 WORD_LIST_2。 我想计算 WORD_LIST_1 中每个单词在 WORK_LIST_2 中任何单词的 X 个单词内的次数。 我的策略是使用正则表达式将 WORD_LIST_1 单词的匹配项提取到一个数组中,然后创建计算每个单词在该数组中的次数的哈希。 当上下文词 (WORD_LIST_2) 跟随 WORD_LIST_1 词时,我可以轻松做到。 但是,当 WORD_LIST_2 单词出现在 WORD_LIST_1 单词之前时,我遇到了问题,特别是当有多个 WORD_LIST_2 单词时。

下面是代码。

#!/usr/bin/perl -w
#use strict;

@colors = ("red", "blue", "green", "brown");

$WORD_LIST_1 = join("|",@colors);

@pets = ("cat","dog","bird","fish");
$WORD_LIST_2 = join("|",@pets);

#$text1 = "The red haired dog quickly and sharply ran away from the blue nosed cat.";
#$text1 = "The green spotted cat drinks blue water.";
#$text1 = "The brown feathered, green beaked bird flew away.";
$text1 = "The fish with blue fins and red tails.";

@finds = ();
$within_N_words = 4;
@finds = $text1 =~ m/\b(?=($WORD_LIST_1)\W+(?:\w+\W+){0,$within_N_words}?(?:$WORD_LIST_2))\b|\b(?=(?:$WORD_LIST_2)\W+(?:\w+\W+){0,$within_N_words}?($WORD_LIST_1))\b/gi;

@finds = grep defined, @finds;

print "\n\n", join("|", @finds), "\n\n";

请注意,第四行 $text1 后面有蓝色和红色的鱼。但它只返回“蓝色”,也不返回“红色”。我检查了前三个被注释掉的句子,它们似乎运行良好。

我的方法是基于这个页面:http://www.regular-expressions.info/near.html

我考虑过的想法包括使用积极的后视,但我需要在后视中使用可变长度。

我考虑过反转整个文本字符串和正则表达式,然后再次搜索。但这可能会导致重复计算。

我还考虑过使用某种循环在单个常规 expersions 中搜索每个 WORD_LIST_1 单词。 但是,这需要花费大量时间处理我的真实数据,因为实际的 WORD_LIST_1 列表大约有 500 个单词,而且我有多个要搜索的长度文本。

另外两个旁注:

(1) 上面的正则表达式偶尔会在@finds 数组中返回空元素。我不知道为什么。我的解决方法是使用 grep 定义的行。解决这个问题的正确方法是什么。相反,为什么我的正则表达式返回空白元素?

(2) 我仍在学习使用 PERL 的“正确”方法。我在这个例子中注释掉了 use strict ,因为我不相信在我使用 perl 的上下文中它会有所作为。我相信有人可以告诉我为什么这是我的错。优秀的 PERL 程序员似乎总是告诉我,我不应该在不使用 strict 的情况下运行 perl 代码,但还没有人说服我这是我需要担心的事情。但是,我愿意学习。

【问题讨论】:

  • 我不太确定正则表达式真的是这项工作的工具。充其量你最终会得到一些极其复杂的语法 - 正如你所发现的那样 - 真的很难解开。
  • 另外:你为什么不相信优秀程序员的意见?普遍的观点是 - strictwarnings 应该是您的第一个故障排除步骤,因为它们会突出一些“有效”但不会产生预期结果的错误。

标签: regex perl


【解决方案1】:

嗯,首先 - 你给出的文字......看起来red首先与fish相距超过4个字?

但是失败了-我认为问题是因为您的正则表达式在第一个匹配项上“消耗”了文本,所以它不是第二个匹配项。

在此,您开始遇到正则表达式引擎的限制 - http://www.regular-expressions.info/keep.html

使用单个正则表达式进行搜索有多重要?请记住,虽然正则表达式 看起来 非常简洁,但它可能难以阅读并且计算量很大。

因此,我建议您拆分模式的最初建议并不像听起来那么糟糕 - 为了匹配第二个示例中的“红色”和“蓝色”,您需要考虑以下条件:将允许重复匹配。

例如

 fish cat red red blue blue

您应该获得多少点击?您可以使用哈希之类的东西来计算重复的单词并删除重复的“关系”:

my %matches = (
        $text1 =~ m/
                       \b
                       ($WORD_LIST_2)
                       \W+
                       (?:\w+\W+){0,$within_N_words}?
                       ($WORD_LIST_1)\b
                   /gix
);

print Dumper \%matches;

我们匹配成一个哈希,因为当我们“插入”成对的单词时,我们会得到键值对:

$VAR1 = {
          'fish' => 'blue'
        };

但是知道它可能是有用的——你可以在 perl 中使用qr 来“编译”一个正则表达式,看看你最终会得到什么。

在你的例子中:

print qr /\b(?=($WORD_LIST_1)\W+(?:\w+\W+){0,$within_N_words}?(?:$WORD_LIST_2))\b|\b(?=(?:$WORD_LIST_2)\W+(?:\w+\W+){0,$within_N_words}?($WORD_LIST_1))\b/;

(?^:\b(?=(red|blue|green|brown)\W+(?:\w+\W+){0,4}?(?:(?^:cat|dog|bird|fish)))\b|\b(?=(?:(?^:cat|dog|bird|fish))\W+(?:\w+\W+){0,4}?(red|blue|green|brown))\b)

第一个模式根本不匹配。 第二个确实如此,但只有一次,因为它“吃掉”了现有的模式。

my @finds2 = ( $text1 =~ m/\b(?:$WORD_LIST_2)\W+(?:\w+\W+){0,$within_N_words}?($WORD_LIST_1)\b/gi )

找到blue。去掉“nongreedy”修饰符,它会找到red。但是因为您的模式已经“吃掉”了前面的位,所以它不能与 g 修饰符匹配两次。

我不认为 perl 会在这种情况下支持多重匹配,因为如果你仔细想想,需要的比较数量很快就会变得庞大。

我也会提供:

  • 查看x 修饰符,以便在它们变长时编写正则表达式。
  • 您可以编译正则表达式,并且在使用实际上是静态的变量(就像您一样)时很有优势。

所以是这样的:

my @pets = qw (cat dog bird fish );
my $WORD_LIST_2 = join( "|", map {quotemeta} @pets );
$WORD_LIST_2 = qr/$WORD_LIST_2/;

my @finds2 = (
    $text1 =~ m/
                   \b
                   (?:$WORD_LIST_2)
                   \W+
                   (?:\w+\W+){0,$within_N_words}?
                   ($WORD_LIST_1)\b
               /gix
);

对于 1:因为您的捕获是交替的“双方”,但只有一个可以匹配。所以不返回undef的那个。将你的模式分成两部分,你就不会有这个问题。或者使用?| 进行分支重置。 http://www.effectiveperlprogramming.com/2010/09/use-branch-reset-grouping-to-number-captures-in-alternations/

对于 2:Why use strict and warnings?

所以我建议以这样的方式结束:

#!/usr/bin/perl 
use strict;
use warnings;
use Data::Dumper;

my @colors = qw ( red blue green brown );    
my $WORD_LIST_1 = join( "|", map {quotemeta} @colors );
   $WORD_LIST_1 = qr/$WORD_LIST_1/;

my @pets = qw (cat dog bird fish );
my $WORD_LIST_2 = join( "|", map {quotemeta} @pets );
   $WORD_LIST_2 = qr/$WORD_LIST_2/;

my $within_N_words = 4;

while ( my $text1 = <DATA> ) {

    print $text1;

    my %matches = (
        $text1 =~ m/(?|                                       
                        \b                                #word break
                          ($WORD_LIST_2) 
                          \W+
                          (?:\w+\W+){0,$within_N_words}?   #nongreedy 0-N 'words'. 
                          ($WORD_LIST_1) 
                        \b
                      |
                        \b
                            ($WORD_LIST_1) 
                            \W+
                            (?:\w+\W+){0,$within_N_words}?
                            ($WORD_LIST_2)
                        \b
                      )
                    /gix
    );

    print Dumper \%matches;
}

__DATA__
The red haired dog quickly and sharply ran away from the blue nosed cat.
The green spotted cat drinks blue water.
The brown feathered, green beaked bird flew away.
The fish with blue fins and red tails.

这给了我们单词和上下文:

The red haired dog quickly and sharply ran away from the blue nosed cat.
$VAR1 = {
          'blue' => 'cat',
          'red' => 'dog'
        };
The green spotted cat drinks blue water.
$VAR1 = {
          'green' => 'cat'
        };
The brown feathered, green beaked bird flew away.
$VAR1 = {
          'brown' => 'bird'
        };
The fish with blue fins and red tails.
$VAR1 = {
          'fish' => 'blue'
        };

(您可以使用values 仅提取单词)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-11-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多