2

为了缩短一些名称,但仍然保持它们的可读性,我想从字符串中删除所有元音,除了第一次和最后一次出现。例如,我希望“Minnesota”变成“Minnsta”。

my $name="Minnesota";

我尝试使用 Perl 的零宽度后视正则表达式语法,如下所示:

$name =~ s/(?<=[aeiou])([^aeiou]*)[aeiou]/$1/ig; # minnst

然而,虽然这会正确处理第一个元音,但它会删除最后一个元音。为了解决这个问题,我尝试保留最后一个元音,如下所示:

$name =~ s/(?<=[aeiou])([^aeiou]*)([aeiou])([aeiou][^aeiou]*)$/$1$3/ig; # minnesota

这也不起作用,大概是因为 '$' 将整个正则表达式锚定到字符串的末尾。

当然,我可以查找第一个元音的位置,反转字符串的其余部分并删除除“第一个”(最后一个)之外的所有元音,然后重新反转并连接字符串,但这不是很优雅。我觉得我忽略了零宽度语法的一个选项。

4

3 回答 3

2

只需为您的正则表达式指定一个结束边界条件(?![^aeiou]*$)::

use strict;
use warnings;

my @words = qw(Minnesota concatenate strings elegant I feel overlooking options syntax any greatly appreciated);

for (@words) {
    my $word = $_;

    $word =~ s/(?<=[aeiou])([^aeiou]*)[aeiou](?![^aeiou]*$)/$1/ig;

    printf "%-12s -> %s\n", $_, $word;
}

输出:

Minnesota    -> Minnsta
concatenate  -> conctnte
strings      -> strings
elegant      -> elgant
I            -> I
feel         -> feel
overlooking  -> ovrlking
options      -> optons
syntax       -> syntax
any          -> any
greatly      -> greatly
appreciated  -> apprcted
于 2014-08-07T18:45:36.380 回答
1

对我来说,这个有效(前面的“1”是故意的):

1 while ($name =~ s/^(.+)[AEIOUaeiou]/$1/g );

如果你想保持 $name 的最小长度(例如 3):

1 while (length $name > 3 && $name =~ s/^(.+)[AEIOUaeiou]/$1/g );

您当然可以使用 'i' 标志来忽略大小写,而不是写 'AEIOUaeiou'。为了便于阅读,我明确地把它写下来。

您当然可以将任何字符放在括号中。

于 2017-06-28T13:12:30.863 回答
0

确保 MATCH 后有一个元音,但将其从 MATCH 中排除。

$name =~ s/(?<=[aeiou])([^aeiou]*)[aeiou](?=.*[aeiou])/$1/ig;

您的正则表达式所做的替换是:

  • 明尼苏达 => nne -> nn => 明尼苏达
  • 明尼苏达 => nnso -> nns => 明斯塔
  • Minnsta => nnsta -> nnst => Minnst
  • Minnst => nnsta -> nnst => Minnst

所以最后一个替换将 'nnsta' 与 'nnst' 交换。

my $name="Minnesota";
my $prev = '';
while ( $name ne $prev ) {
    $prev = $name;
    $name =~ s/(?<=[aeiou])([^aeiou]*)[aeiou]/$1/i;
    print "$prev => ${^MATCH} -> $1 => $name\n";
}
于 2014-08-07T19:26:06.620 回答