debiandude has asked for the wisdom of the Perl Monks concerning the following question:

I have a string that is eight numbers and I would like to split it into two. I know that I could do it with substring but split seemed like a more logical choice. One way I did it was this:

my $thing = '12340019'; $thing = s/(\d{4})(\d{4})/$1 $2/; my @splat = split(/ /, $thing);

But that doesn't really seem like the best way to do it. I figured that I should be able to get the effect by using split by itself and asking it to split on 4 numbers, but I couldn't get it to work. Also how would I go about changing '0019' into 19. Is sprintf the only (or best) way to accomplish that.

Thanks

Replies are listed 'Best First'.
Re: Problem With Split
by hv (Prior) on Jun 16, 2004 at 13:30 UTC

    split is best used when you have a separator of some sort to split on. In this case there is no separator - you have a bunch of things packed together - so you'd probably be better off with unpack:

    my @splat = unpack '(A4)*', $thing;

    That is: unpack this string into (4-character substrings) as many as you can find. (If there can only be two such substrings, you can replace the '*' with '2' to specify that.) Note that this is just dealing with characters, it doesn't know anything about digits - if you need to locate the digits in the string first, you probably want a regular expression for that.

    Once you have the right digits separated out, removing leading zeros is as simple as getting perl to treat it as a number. A couple of common idioms for doing that are:

    $num = int($string); $num = $string + 0;

    With map you can apply that transformation to the stream of strings on its way into the array:

    my @splat = map $_ + 0, unpack '(A4)*', $thing;

    Hope this helps,

    Hugo

      Ahh.. I was unaware of the unpack function. Pretty cool. Thanks.
Re: Problem With Split
by tinita (Parson) on Jun 16, 2004 at 12:35 UTC
    how about:
    my $thing = '12340019'; my @splat = $thing =~ m/\d{4}/g;
    my $num = '0019'; $num = int $num;
    A reply falls below the community's threshold of quality. You may see it by logging in.
Re: Problem With Split
by pbeckingham (Parson) on Jun 16, 2004 at 13:02 UTC

    Did you want integers?

    my $thing = '12340019'; my @splat = map {int $_} $thing =~ /(\d{4})/g

Re: Problem With Split
by Happy-the-monk (Canon) on Jun 16, 2004 at 12:41 UTC
Re: Problem With Split
by wufnik (Friar) on Jun 16, 2004 at 13:33 UTC
    you could try unpack for this, given you seem to be looking for fixed length string processing. after all, why use vulgar regexen when the refined unpack is ready to help?

    in the following i also use sprintf to deal with leading zeros.

    print join ":", map {sprintf("%d",$_)} unpack("A4A4",shift);
    ...wufnik

    -- in the world of the mules there are no rules --
Re: Problem With Split
by periapt (Hermit) on Jun 16, 2004 at 13:07 UTC
    If you are sure that your input will always be integers, you could use
    my $thing = '12340019'; my @num01 = map{ int } $thing =~ m/(\d{4})/g;

    PJ
    We are drowning in information and starving for knowledge - Rutherford D. Rogers
    What good is knowledge if you have to pull teeth to get it - anonymous
Re: Problem With Split
by Roger (Parson) on Jun 16, 2004 at 13:29 UTC
    You can combine look behind and look ahead in the regex of the split:

    #!/usr/local/bin/perl -w use strict; my $number = '00120034'; # split at the position that has 4 digits # behind and 4 digits ahead my ($n1, $n2) = split /(?<=\d{4})(?=\d{4})/, $number; print "$n1, $n2\n"; # strip leading 0's ($n1, $n2) = map { int $_ } ($n1, $n2); print "$n1, $n2\n";

    And the output is as expected...
    0012, 0034 12, 34


    This technique is very useful. For example, you could comma'fy a floating point number with look ahead and look behind in one go, sort of... :-)
    { local $_ = $amount; /\./ ? s/(?<=\d)(?=(\d{3})+(?:\.))/,/g : s/(?<=\d)(?=(\d{3})+(?!\d)) +/,/g; $amount = $_; }

      Maybe you should submit that as a Q&A answer.

      Here's another stab:

      s{(?<!\d|\.)(\d+)} {my $n=$1; $n=~s/(?<=\d)(?=(?:\d{3})+$)/,/g; $n }e;

      We're not really tightening our belts, it just feels that way because we're getting fatter.
      This technique is very useful

      Lookahead and lookbehind are both very useful indeed, but I think your example is better solved with a sexeger:

      $amount = reverse $amount; $amount =~ s/(\d{3})/$1,/g; $amount = reverse $amount;

      Update: oops, I guess my example doesn't commafy a floating-point number, now does it? Oh well. At least maybe someone will learn about sexegers who didn't know about them. 8^)