2

已经N定义了单词,对于这个问题 3 个单词,例如:open icebreaker umbrela

想知道这里是否存在任何可能的首字母缩略词作为英文单词存在,例如想要运行类似:

grep -Pi '^o(p(e?))?i(c(e?))?um?$' my_long_wordlist.txt

在上面的正则表达式中我决定然后我可以使用

  • 从第一个词o,或opope(第一个,或前两个,或前三个字母)
  • 从第二个单词i,或icice(第一个,或前两个,或前三个字母)
  • 最后形成最后一个词,我只能使用第一个或前两个字母 -uum

为了好玩 - 上面的正则表达式将为我返回这个词:opium:)

手动构建正则表达式对于两个测试是可以接受的,但我想检查许多单词组合,所以,寻找一种如何生成正则表达式的方法,如上所述。

想要通过以下调用构建“首字母缩略词查找器正则表达式脚本”:

acrobuild open:4 icebreaker:3 umbrela:3

如您所见,args 是单词,分隔符后面的数字是首字母缩写词中可以使用的最大字母数。

现在的问题 - 我完全失去了如何为给定长度构建正则表达式。需要一些提示、想法或类似的东西.. - 检查“这里需要帮助”:)

目前我有这个:

#!/usr/bin/perl

use 5.012;
use strict;
use warnings;

do_grep(  make_regex(@ARGV) );
exit;

sub make_regex {
    my(@words) = @_;
    my $regex;
    foreach my $wordnum (@words) {
        $regex .= make_word_regex( split(/:/, $wordnum) );
    }
    $regex = '^' . $regex . '$' if $regex;
    return $regex;
}

sub make_word_regex {
    my($word, $num) = @_;

    return "" unless $word;
    $num = length($word) unless defined($num);  #for make legal -> word:0

    my(@chars) = split(//, substr($word,0,$num) );

    #regex building x  or   xy?  or  x(y(z?))? etc... :(
    my $re = "";
    foreach my $c (reverse(@chars)) {   #reverse, to building inside-out
        # HOW TO BUILD THE REGEX HERE?
        # NEED HELP HERE
    }
    return($re);
}

sub do_grep {
    my($re) = @_;
    say "$re"; return; #tmp
    my $recomp = qr/$re/i;

    open(my $fdict, "<", "/usr/share/dict/web2") or die("No dict file $!");
    while(<$fdict>) {
        chomp;
        say $_ if m/$recomp/;
    }
    close($fdict);
}
4

2 回答 2

2

而不是嵌套的 regexp o(p(e?)?),我只会列出一个替代列表:(o|op|ope).

sub make_regex_word {
    my ($word)=@_;
    my ($base,$count)=split(/:/,$word);
    my @chars=split(//,$base);
    my @re=();
    for ($i=0;$i<$count;$i++) {
        push @re,join("",@chars[0..$i]);
    }
    return "(".join("|",@re).")";
}
于 2013-04-18T18:54:26.420 回答
2

你通常是在正确的轨道上。我会这样实现make_word_regex

my ($word, $num) = @_;

# paranoid error checking
defined $word         or croak "Can't prepare undef value";
$num <= length($word) or croak "More characters requested than avaliable";
$num >= 1             or croak "Pattern must consist of at least one char";

my $regex = ''; # initialize $regex to something we can interpolate w/o warning

for my $char (reverse split //, substr $word, 0, $num) {
  # use qr// instead of treating regexes like strings
  # The \Q ... \E protects for special characters. Always use this for external input.
  $regex = qr/\Q$char\E $regex?/x; 
}

return $regex;

这可以按预期工作,除了正则表达式中包含许多不必要的垃圾(make_word_regex("open", 3)返回一个可能字符串化的正则表达式对象(?x-ism:o (?x-ism:p (?x-ism:e ?)?)?),具体取决于您的 perl)。

您可以以类似的方式将这些部分正则表达式组合到首字母缩略词查找器中。我会make_regex写成

# assert that every word is followed by a number.
@_ % 2 == 0 or croak "even number of arguments required.";

my @regexes;
while (@_) {
  my ($word, $num) = splice @_, 0, 2; # shift the first two elems
  push @regexes, make_word_regex($word, $num);
}

# combine the regexes:
return qr/ \A  @regexes \z /x;

\A字符串开头的锚点;\z在最后。该/x标志允许通过包含不匹配的空格使正则表达式更具可读性。

然后你可以调用脚本

$ acrobuild open 3 icebreaker 3 umbrella 2

我建议不要对字典文件进行硬编码。改为通过 STDIN 管道 dict:

$ acrobuild open 3 icebreaker 3 umbrella 2 </usr/share/dict/web2

这将简化您do_grep

my $re = shift;
while (<STDIN>) {
  chomp;
  say if /$re/i;
}
于 2013-04-18T18:55:17.457 回答