Take the 2-minute tour ×

I have a string aa:bb::cc:yy:zz which needs to be split in such a way that I have an array with aa:bb::cc, yy, zz. i.e. I want to create two substrings from last with : as delimiter and remaining as a element of an array. What's the best way to achieve this?


aa:bb::cc:yy:zz --> ['aa:bb::cc','yy','zz']

dd:ff:gg:dd:ee:ff:fg --> ['dd:ff:gg:dd:ee','ff','gg']

I store IP address:port:protocol as a key in a file , and splitting wiht ":" to get IP,port,proto back and things were working fine when IP address is limited to Ipv4. Now I want to make it ported to Ipv6 in which case IP address contains ":" so I can't get proper IP address by splitting with ":".

share|improve this question
It would be safer to store your (IP,port) pairs using the standardized [IP address]:port format. The parse_hostport/format_hostport in the AnyEvent::Socket module are helpful to handle it. –  dolmen Jan 15 '12 at 18:38
add comment

5 Answers

up vote 11 down vote accepted

How about:

use Data::Dump qw(dump);
use strict;
use warnings;

my $x = 'dd:ff:gg:dd:ee:ff:fg';
my @l = $x =~ /^(.*?):([^:]+):([^:]+)$/g;
dump @l;


("dd:ff:gg:dd:ee", "ff", "fg")
share|improve this answer
Note that this will fail on 'two or less pairs' string, like 'dd:ff' or 'dd' –  yko Sep 7 '11 at 13:21
that's not a problem for me as I would expect miminum of 3 parameters. –  kumar Sep 7 '11 at 13:29
THanks M42, perfect solution for me. –  kumar Sep 7 '11 at 13:29
@kumar: You're welcome. –  M42 Sep 7 '11 at 13:31
add comment
$ perl -wE '$_="aa:bb::cc:yy:zz"; say join "\n", split /:([^:]+):([^:]+)$/, $_;'

Update: You did not mention this was meant to parse IP addresses. If it is, you would probably be better off trying to find a module on CPAN

share|improve this answer
Thanks TLP, this also works like a charm. –  kumar Sep 7 '11 at 14:05
@kumar You are welcome. It's basically the same solution as M42. –  TLP Sep 7 '11 at 14:09
add comment

This code will correct handle situations when $string contains 2 or less pairs:

my $string = 'aa:bb::cc:yy:zz';
my @data = split /:/, $string;
if (@data > 2) {
    unshift @data, join ':', splice @data, 0, -2;

# $string = 'aa:bb::cc:yy:zz';
# @data contains ('aa:bb::cc', 'yy', 'zz')

# $string = 'aa:bb';
# @data contains ('aa', 'bb')
share|improve this answer
add comment
$ perl -e'$_="aa:bb::cc:yy:zz"; @f=/(.*):([^:]+):(.+)/; print "$_\n" for @f'

$ perl -e'$_="dd:ff:gg:dd:ee:ff:fg"; @f=/(.*):([^:]+):(.+)/; print "$_\n" for @f'
share|improve this answer
add comment

I'd do an overly aggressive split followed by a join. I think the result is much more readable when you're not using a complicated regex for the split. So:

my $string = 'aa:bb::cc:yy:zz';
my @split_string = split(/:/, $string);
my @result = (join(':', @split_string[0..scalar(@split_string)-3]), $split_string[-2], $split_string[-1]);

Gives you:

aa:bb::cc, yy, zz

You'd have to do some array bounds checking on @split_string before you start indexing it like that.

share|improve this answer
add comment

Your Answer


