为了缩短一些名字,但仍然保持一定的可读性,我想从字符串中删除所有元音,除了第一次和最后一次出现。例如,我喜欢'明尼苏达州'成为Minnsta'。
my $name="Minnesota";
我尝试使用Perl的零宽度后视正则表达式语法,如下所示:
$name =~ s/(?<=[aeiou])([^aeiou]*)[aeiou]/$1/ig; # minnst
然而,虽然这适当地处理了第一个元音,但它删除了最后一个元音。 为了解决这个问题,我试着保留最后一个元音,如下所示:
$name =~ s/(?<=[aeiou])([^aeiou]*)([aeiou])([aeiou][^aeiou]*)$/$1$3/ig; # minnesota
这也没有用,大概是因为&#39; $&#39;将整个正则表达式锚定到字符串的末尾。
当然,我可以查找第一个元音的位置,反转其余的字符串并删除所有元音,除了“第一个元音”。 (最后),并重新反转和连接字符串,但这不是很优雅。我觉得我忽略了零宽度语法的一个选项。
答案 0 :(得分: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
答案 1 :(得分:1)
对我来说,这个有效(前面的'1'是故意的):
1 while ($name =~ s/^(.+)[AEIOUaeiou]/$1/g );
如果你想保留$ name的最小长度(例如3):
1 while (length $name > 3 && $name =~ s/^(.+)[AEIOUaeiou]/$1/g );
您可以使用'i'标志来忽略此情况,而不是写'AEIOUaeiou'。我明确地写下来以便于阅读。
您当然可以在括号中添加任何字符。
答案 2 :(得分:0)
确保在MATCH之后有一个元音,但将其从MATCH中排除。
$name =~ s/(?<=[aeiou])([^aeiou]*)[aeiou](?=.*[aeiou])/$1/ig;
正则表达式完成的替换是:
所以最后一次替换用'nnst'交换'nnsta'。
my $name="Minnesota";
my $prev = '';
while ( $name ne $prev ) {
$prev = $name;
$name =~ s/(?<=[aeiou])([^aeiou]*)[aeiou]/$1/i;
print "$prev => ${^MATCH} -> $1 => $name\n";
}