开发者

How can I split a Perl string only on the last occurrence of the separator?

开发者 https://www.devze.com 2022-12-23 21:32 出处:网络
my $str=\"1:2:3:4:5\"; my ($a,$b)=split(\':\',$str,2); In the above code I have used limit as 2 ,so $a will contain 1 and remaining elements will be in $b.

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

In the above code I have used limit as 2 ,so $a will contain 1 and remaining elements will be in $b. Like this开发者_开发技巧 I want the last element should be in one variable and the elements prior to the last element should be in another variable.

Example

$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"


split(/:([^:]+)$/, $str)


You could use pattern matching instead of split():

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

The first group captures everything up to the last occurence of ':' greedily, and the second group captures the rest.

In case the ':' is not present in the string, Perl is clever enough to detect that and fail the match without any backtracking.


you can also use rindex() eg

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";

output

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


I know, this question is 4 years old. But I found the answer from YOU very interesting as I didn't know split could work like that. So I want to expand it with an extract from the perldoc split that explains this behavior, for the sake of new readers. :-)

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';

From Perldoc:

If the PATTERN contains capturing groups, then for each separator, an additional field is produced for each substring captured by a group (in the order in which the groups are specified, as per backreferences); if any group does not match, then it captures the undef value instead of a substring. Also, note that any such additional field is produced whenever there is a separator (that is, whenever a split occurs), and such an additional field does not count towards the LIMIT. Consider the following expressions evaluated in list context (each returned list is provided in the associated comment):

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')


You can do it using split and reverse as follows:

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

Now $a will be 1:2:3:4 and $b will be 5.

A much simpler and cleaner way is to use regex as Mark has done in his Answer.


I'm a bit late to this question, but I put together a more generic solution:

# 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);
}

It accepts arguments similar to split() except that the splitting is done in reverse order. It also accepts a limit clause in case you need a specified number of result elements.

Note: this subroutine expects a precompiled regex as the first parameter.
Perl's split is a built-in and will interpret /pat/ correctly, but attempting to pass /pat/ to a subroutine will be treated as sub($_ =~ /pat/).

This subroutine is not bulletproof! It works well enough for simple delimiters but more complicated patterns can cause issues. The pattern itself cannot be reversed, only the expression it matches against.


Examples:

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')
0

精彩评论

暂无评论...
验证码 换一张
取 消