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

To all,

I have a string of thirty digits that I need to insert dashes after every 5 digits, and then a <br> after the 15th digit, as in:

012345678901234567890123456789 ==> 01234-56789-01234<br>56789-01234-56789
This is what I've come up with. What are some other alternatives to this regex?
#!/perl/bin/perl -w use strict; my $s = "012345678901234567890123456789"; print $s, "\n"; $s =~ s/(\d{5})(\d{5})(\d{5})(\d{5})(\d{5})(\d{5})/$1-$2-$3<br>$4-$5-$ +6/; print $s, "\n";
Where do you want *them* to go today?

Replies are listed 'Best First'.
Re: A regex question...
by artist (Parson) on May 05, 2003 at 18:39 UTC
      Friedl has a variant of this; 'commafying a number'.
      I did a paraphrase of artist's solution, influenced by Friedl, but with a twist for the <br> insert. And then a cmpthese.
      nacka: s/(\d)(?=(\d{5})+(?!\d))/${1}-/g; substr($_, 17, 1, '<br>'); artist: s/((.){5})(?!$)/$1-/g; s/((.){17})\-/$1\<br\>/; thezip: s/(\d{5}) (\d{5}) (\d{5}) (\d{5}) (\d{5}) (\d{5}) /$1-$2-$3<br>$4-$5-$6/x; nacka/artist hybrid: s/(\d)(?=(\d{5})+(?!\d))/$1-/g; s/((.){17})\-/$1\<br\>/; artist/nacka hybrid: s/((.){5})(?!$)/$1-/g; substr($_, 17, 1, '<br>');
      And the Benchmark:
      Rate artist thezip artist/nacka nacka/artist + nacka artist 13175231/s -- -8% -9% -14% + -14% thezip 14347202/s 9% -- -1% -6% + -7% artist/nacka 14534884/s 10% 1% -- -5% + -6% nacka/artist 15313936/s 16% 7% 5% -- + -0% nacka 15384615/s 17% 7% 6% 0% + --
      All of the above rate numbers are probably within the error margin.

      I declare thezip's solution the most readable, and hence the winning solution.
Re: A regex question...
by chip (Curate) on May 05, 2003 at 18:38 UTC
    I'd work backwards. The last step is obviously a join on "<br>". The step before that would be the insertion of dashes. The step before that would be splitting into fifteen-digit blocks. Hence:

    $s = join '<br>', map { /(\d{5})(\d{5})(.*)/ && "$1-$2-$3" } ($s =~ /\d{15}/g);

        -- Chip Salzenberg, Free-Floating Agent of Chaos

      Ah, I just noticed that it's not a string of arbitrary many fifteen-digit blocks, but just two. So artist has the better solution, for this problem.

          -- Chip Salzenberg, Free-Floating Agent of Chaos

Re: A regex question...
by pfaut (Priest) on May 05, 2003 at 18:40 UTC
    #!/usr/bin/perl -w use strict; my $str = '012345678901234567890123456789'; my @arr1 = $str =~ /(\d{5})/g; my @arr2; while (@arr1) { push @arr2, join('-', splice(@arr1,0,3)) } print join('<br>',@arr2),$/;

    Output:

    01234-56789-01234<br>56789-01234-56789
    90% of every Perl application is already written.
    dragonchild
Re: A regex question...
by Enlil (Parson) on May 05, 2003 at 19:03 UTC
    yet another way to do it:
    use strict; use warnings; my $string= "012345678901234567890123456789"; $string = sprintf "%5s-%5s-%5s<br>%5s-%5s-%5s",$string=~/(\d{5})/g; print $string,$/;

    update: I saw Tie::Cycle earlier in this node so you could also do it this way:

    use strict; use warnings; use Tie::Cycle; tie my $cycle, 'Tie::Cycle', [ qw( - - <br> ) ]; my $string= "012345678901234567890123456789"; $string =~ s/(\d{5})(?!\b)/$1$cycle/g; print $string;
    -enlil

      Enlil,

      I'd have to say that I like your first solution best of all because it's concise and make it obvious what's going on. I think it will make maintenance easier later on for other folks...

      This is the one-liner I was looking for.

      Enlil++

      Thanks to all who helped!

      Where do you want *them* to go today?
Re: A regex question...
by thelenm (Vicar) on May 05, 2003 at 18:40 UTC
    Your regex looks fine, here's an alternative using substr:
    my $s = '0123456789'x3; print "$s\n"; my $c = 30; substr $s, $c, 0, $c==15?'<br>':'-' while $c-=5; print "$s\n";

    -- Mike

    --
    just,my${.02}

Re: A regex question...
by Abigail-II (Bishop) on May 05, 2003 at 19:01 UTC
    #!/usr/bin/perl use strict; use warnings; sub TIESCALAR {bless \(my $i = 0) => 'main'} sub FETCH {$$_ [0] ++ % 3 ? "-" : "<br>"} tie $" => 'main'; my $num = "0123456789" x 3; my @n = $num =~ /\d{5}/g; print "@n\n"; __END__
Re: A regex question...
by Aristotle (Chancellor) on May 05, 2003 at 18:58 UTC
    local $_ = "012345678901234567890123456789"; print join( '<br>', map join('-',/(\d{5})/g), /(\d{15})/g ), "\n";
    Don'cha just love lists. :)

    Makeshifts last the longest.

Re: A regex question...
by BrowserUk (Patriarch) on May 05, 2003 at 22:30 UTC

    I think that a couple of small mods to your own solution make it very clear and readable. First, if you know they are digits, no need to specify that. Second, use paired delimiters and lay it out over two lines

    $s = s[(.{5}) (.{5}) (.{5}) (.{5}) (.{5}) (.{5})] [$1-$2-$3<br />$4-$5-$6]x;

    If the /x modifier also operated in the replacement, I would line the bits up vertically too.

    The only other change I might make is if the regex was used in more than one place, I would probably compile it with qr// and give it a meaningful name.


    Examine what is said, not who speaks.
    "Efficiency is intelligent laziness." -David Dunham
    "When I'm working on a problem, I never think about beauty. I think only how to solve the problem. But when I have finished, if the solution is not beautiful, I know it is wrong." -Richard Buckminster Fuller
      If the /x modifier also operated in the replacement, I would line the bits up vertically too.
      You could add /e and build a string with dot concatenations.

      Makeshifts last the longest.

Re: A regex question...
by apsyrtes (Beadle) on May 05, 2003 at 18:57 UTC
    I think what you have is perfect. It works, which is a good thing for you, and it's self-explaining, which is a great thing for anybody coming after you.

    Is there any particular reason you need an alternative? Jason W.
Re: A regex question...
by thor (Priest) on May 05, 2003 at 20:30 UTC
    Here's my stab:
    my $string = "012345678901234567890123456789"; my @array = unpack("A5" x 6, $string); printf("%s-%s-%s<br>%s-%s-%s\n", @array);
    Of course, you could inline the unpack in to the printf, if you so chose. The bonus (or maybe not, depending on your situation) is that this works with letters and numbers.

    thor

Re: A regex question...
by artist (Parson) on May 05, 2003 at 19:12 UTC
    Too many responses: From simple to complex as using 'tie'. What the op thezip has in mind while searching for alternative ? Simplicity, Readability, Short term usability, Long term usability, Finding newer ways of doing same thing..?

    Artist

Re: A regex question...
by fletcher_the_dog (Friar) on May 05, 2003 at 21:34 UTC
    Here's another simple solution
    my $s = "012345678901234567890123456789"; print $s, "\n"; $i=0; $s =~ s/(\d{5})/$1.(++$i==3 && "<BR>" or $i!=6 && "-")/ge; print $s, "\n";
      s/\d{5}(?!$)/$& . (++$i%3?'-':'<br>')/eg

      Is probably slightly more generic that $i!=6 :)
      (edit: string of 30 digits! $i!=6 is fine)
      But I prefer:

      $s =~ s/\B/ !(++$i%15) && '<br>' || !($i%5) && '-' /eg;

      Jasper
Re: A regex question...
by gaspodethewonderdog (Monk) on May 06, 2003 at 14:49 UTC
    The only thing that I would suggest would be to possibly do some error handling to make sure that the data is what you expect... besides that there really isn't anything wrong with the code you came up with.