【问题标题】:How can I split a Perl string only on the last occurrence of the separator?如何仅在最后一次出现分隔符时拆分 Perl 字符串?
【发布时间】:2011-01-28 23:11:43
【问题描述】:

我的 $str="1:2:3:4:5"; 我的 ($a,$b)=split(':',$str,2);

在上面的代码中,我使用了 limit 作为 2 ,所以 $a 将包含 1,其余元素将在 $b 中。 像这样我希望最后一个元素应该在一个变量中,最后一个元素之前的元素应该在另一个变量中。

示例

$str = "1:2:3:4:5" ; 
# $a should have "1:2:3:4"  and $b should have "5" 
$str =  "2:3:4:5:3:2:5:5:3:2" 
# $a should have "2:3:4:5:3:2:5:5:3" and $b should have "2"

【问题讨论】:

标签: perl split


【解决方案1】:

您可以使用模式matching 代替split()

my ($a, $b) = $str =~ /(.*):(.*)/;

第一组贪婪地捕获直到最后一次出现 ':' 的所有内容,第二组捕获其余部分。

如果':' 出现在字符串中,Perl 足够聪明地检测到这一点,并在没有任何回溯的情况下使匹配失败。

【讨论】:

  • 我会把第二个 .* 变成 .?,只是为了确定。
【解决方案2】:

这个问题我有点晚了,但我整理了一个更通用的解决方案:

# Similar to split() except pattern is applied backwards from the end of the string
# The only exception is that the pattern must be a precompiled regex (i.e. qr/pattern/)
# Example:
#   rsplit(qr/:/, 'John:Smith:123:ABC', 3) => ('John:Smith', '123', 'ABC')
sub rsplit {
    my $pattern = shift(@_);    # Precompiled regex pattern (i.e. qr/pattern/)
    my $expr    = shift(@_);    # String to split
    my $limit   = shift(@_);    # Number of chunks to split into

    # 1) Reverse the input string
    # 2) split() it
    # 3) Reverse split()'s result array element order
    # 4) Reverse each string within the result array
    map { scalar reverse($_) } reverse split(/$pattern/, scalar reverse($expr), $limit);
}

它接受类似于split() 的参数,只是拆分是以相反的顺序完成的。如果您需要指定数量的结果元素,它还接受限制子句。

注意:此子例程需要 precompiled regex 作为第一个参数。
Perl 的split 是内置的,可以正确解释/pat/,但尝试将/pat/ 传递给子例程将被视为sub($_ =~ /pat/)

这个子程序不是防弹的!它适用于简单的分隔符,但更复杂的模式可能会导致问题。模式本身不能反转,只能反转它匹配的表达式。


例子:

rsplit(qr/:/, 'One:Two:Three', 2); # => ('One:Two', 'Three')

rsplit(qr/:+/, 'One:Two::Three:::Four', 3); # => ('One:Two', 'Three', 'Four')

# Discards leading blank elements just like split() discards trailing blanks
rsplit(qr/:/, ':::foo:bar:baz'); # => ('foo', 'bar', 'baz')

【讨论】:

  • 我看不出这个解决方案的重点或“更通用”的方面,它在内部使用split,并两次调用reverse。当my ($a, $b) = split /:([^:]+)$/, $str; 工作得很好时,这一切有什么意义?
【解决方案3】:

我知道,这个问题已经 4 岁了。但我发现YOU 的答案非常有趣,因为我不知道split 可以这样工作。因此,为了新读者,我想用 perldoc split 的摘录来扩展它,解释这种行为。 :-)

my $str = "1:2:3:4:5";
my ($a, $b) = split /:([^:]+)$/, $str;
# Capturing everything after ':' that is not ':' and until the end of the string
# Now $a = '1:2:3:4' and $b = '5';

来自Perldoc

如果 PATTERN 包含捕获组,则对于每个分隔符,为组捕获的每个子字符串生成一个附加字段(按照指定组的顺序,根据反向引用);如果任何组不匹配,则它捕获 undef 值而不是子字符串。另外,请注意,只要有分隔符(即发生拆分时),就会生成任何此类附加字段,并且此类附加字段不计入 LIMIT。考虑在列表上下文中评估的以下表达式(每个返回的列表都在相关注释中提供):

split(/-|,/, "1-10,20", 3)
# ('1', '10', '20')

split(/(-|,)/, "1-10,20", 3)
# ('1', '-', '10', ',', '20')

split(/-|(,)/, "1-10,20", 3)
# ('1', undef, '10', ',', '20')

split(/(-)|,/, "1-10,20", 3)
# ('1', '-', '10', undef, '20')

split(/(-)|(,)/, "1-10,20", 3)
# ('1', '-', undef, '10', undef, ',', '20')

【讨论】:

    【解决方案4】:

    您也可以使用rindex() 例如

    my $str="1:2:3:4:5";
    $i=rindex($str,":");
    $a=substr($str,0,$i);
    $b=substr($str,$i+1);
    print "\$a:$a, \$b: $b\n";
    

    输出

    $ perl perl.pl
    $a:1:2:3:4, $b: 5
    

    【讨论】:

    • 由于在这种情况下拆分分隔符非常简单,因此与使用正则表达式解析尝试绑定到$ 的整个表达式相比,这是一种更快的解决方案。
    【解决方案5】:

    您可以使用 split 和 reverse 来做到这一点,如下所示:

    my $str="1:2:3:4:5";
    my ($a,$b)=split(':',reverse($str),2); # reverse and split.
    
    $a = reverse($a); # reverse each piece.
    $b = reverse($b);
    
    ($a,$b) = ($b,$a); # swap a and b
    

    现在$a 将变为1:2:3:4$b 将变为5

    一个更简单、更简洁的方法是使用正则表达式,就像 Mark 在他的答案中所做的那样。

    【讨论】:

    • 虽然这是可能的,但它并不完全有效,尤其是当单行就可以做到这一点时。类似my ($a,$b) = ($str =~ /(.*):(.?)/);
    【解决方案6】:
    split(/:([^:]+)$/, $str)
    

    【讨论】:

    • 这将受益于解释,就像 Francisco Zarabozo 在另一个答案中提供的那样。
    猜你喜欢
    • 2019-01-06
    • 2014-10-08
    • 2021-12-08
    • 2020-08-11
    • 1970-01-01
    • 2017-02-23
    • 1970-01-01
    • 1970-01-01
    • 2014-01-12
    相关资源
    最近更新 更多