给出以下文本和掩码字符串: -
text: the quick brown fox jumps over the lazy dog
mask: xx xxx xxxx x xxx
我试图找到一种简洁的方法来得出结果: -
th qui brow f jum
掩码使文本符合它的模式。结果字符串应与掩码具有相同数量的单词。
我目前的实现是使用List::Zip
将每个列表的单词压缩在一起并进行字符串替换。 (我已将zip功能的逻辑复制到下面的示例中,因此您不需要安装它来进行测试)
# Squashed version of List::Zip->zip function
sub zip{map{[map{shift@{$_}}@_]}0..((sort map{0+@{$_}}@_)[0]-1)}
my $mask = 'xx xxx xxxx x xxx';
my $text = 'the quick brown fox jumps over the lazy dog';
for my $mt ( zip( [split(' ', $mask)], [split(' ', $text)] ) ) {
my ( $m, $t ) = @{ $mt };
$mask =~ s/ $m / substr( $t, 0, length($m) ) /xe;
}
print $mask; # OUTPUT: th qui brow f jum
...但我无法帮助,但认为这是一种较短的方式。也许是一个时髦的正则表达技巧?
建议欢迎。
更新
接受的答案here对于pos
的使用很有吸引力。试图弄清楚如何将它应用于我的问题。 (编辑:鲍罗丁指出为什么它不适用于这个问题)
我还应该注意到任意的空格都没关系,即给出:
text: 'one two three'
mask: 'x xx xxx'
我不在乎返回的结果是o tw thr
。唯一的要求是相同数量的单词和相同长度的单词。
审核
最后,我接受了亚历山大的时髦正则表达式'解。它简洁而且非常快,以最快的速度运行最快的基准测试。
鲍罗丁的第一个解决方案虽然非常相似,却创造了一个也没有表现出来的正则表达式。
Borodin: (\S{1,2}) \S*\s+ (\S{1,3}) \S*\s+ (\S{1,4}) \S*\s+ (\S{1,1}) \S*\s+ (\S{1,3})
Alexandr: (\S{2})\S*\s+(\S{3})\S*\s+(\S{4})\S*\s+(\S{1})\S*\s+(\S{3})\S*
对鲍罗丁的解决方案进行了一些小改动,使其与亚历山大相提并论,但我把它送给了亚历山大,首先到达那里。
所有的解决方案都充满了伟大而有趣的想法,谢谢大家。
答案 0 :(得分:2)
这看起来像是一个单词正则表达匹配,将x
翻译为.
。
所以:
#!/usr/bin/env perl
use strict;
use warnings;
use Data::Dumper;
my $text = 'the quick brown fox jumps over the lazy dog';
my $mask = 'xx xxx xxxx x xxx';
#split mask on whitespace
my @match = split ' ', $mask;
print Dumper \@match;
#iterate each word in $text
foreach my $word ( split ' ', $text ) {
#if no more 'match' entries, end.
last unless @match;
#grab 'first' match pattern. and turn `xx` into `..`
#e.g. a regex matching any 2 characters.
my $pattern = (shift @match) =~ s/x/./rg;
#trim words to match pattern
print $word =~ s/^($pattern).*/$1/r, " ";
}
(注意 - r
正则表达式标志是一个更新的功能。如果它在你的perl版本中不起作用,那么你可以在循环之外s/x/./g for @match
。
答案 1 :(得分:2)
以下是两种方式
首先,将$mask
值转换为正则表达式会得到正确的结果
use strict;
use warnings 'all';
use feature 'say';
my $text = 'the quick brown fox jumps over the lazy dog';
my $mask = 'xx xxx xxxx x xxx';
my $re = join ' \S*\s+ ', map { sprintf '(\S{1,%d})', length } split ' ', $mask;
$re = qr/$re/xs;
my $new = join ' ', $text =~ $re;
say $new;
其次,这会将$mask
转换为子字符串长度列表,并使用它来转换$text
字符串
use strict;
use warnings 'all';
use feature 'say';
my $text = 'the quick brown fox jumps over the lazy dog';
my $mask = 'xx xxx xxxx x xxx';
my @mask = map length, split ' ', $mask;
my $i = 0;
my $new = join ' ', map { $i > $#mask ? () : substr($_, 0, $mask[$i++]) } split ' ', $text;
say $new;
最后,这里使用List::MoreUtils
pairwise
函数对上面的第二个解决方案进行了更整齐的重写
use strict;
use warnings 'all';
use feature 'say';
use List::MoreUtils 'pairwise';
my $text = 'the quick brown fox jumps over the lazy dog';
my $mask = 'xx xxx xxxx x xxx';
my @text = split ' ', $text;
my @mask = split ' ', $mask;
my $new = join ' ', pairwise { $b ? substr($a, 0, length $b) : () } @text, @mask;
say $new;
答案 2 :(得分:1)
清理正则表达式解决方案,将掩码转换为正则表达式:
use strict;
use v5.10;
my $text = 'the quick brown fox jumps over the lazy dog';
my $mask = 'xx xxx xxxx x xxx';
say $mask;
$mask =~ s/(x+)/ '(\S{'.(length $1).'})\S*'/ge;
$mask =~ s/\s+/\\s+/g;
say $mask;
say join ' ', ($text =~ /^$mask/);