字符串屏蔽:将文本复制到给定的掩码

时间:2016-07-05 13:54:20

标签: perl

给出以下文本和掩码字符串: -

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*

对鲍罗丁的解决方案进行了一些小改动,使其与亚历山大相提并论,但我把它送给了亚历山大,首先到达那里。

所有的解决方案都充满了伟大而有趣的想法,谢谢大家。

3 个答案:

答案 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/);