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

A workmate asked me today what is the best way to transform "A3xyz45M98AB7Z9" into "A3:xyz45:M98:AB7:Z9". I suggested:

my $s = 'A3xyz45M98AB7Z9'; my $t = join(':', $s =~ /[A-Za-z]+\d+/g); print "s='$s' t='$t'\n";
and he seemed happy with that. In the spirit of TMTOWTDI how would you do it?

Replies are listed 'Best First'.
Re: Best way to put : between fields in a string
by Roy Johnson (Monsignor) on Apr 21, 2005 at 10:50 UTC
    Put a colon after a digit and before a non-digit:
    s/(?<=\d)(?=\D)/:/g;

    Caution: Contents may have been coded under pressure.
Re: Best way to put : between fields in a string
by polettix (Vicar) on Apr 21, 2005 at 11:03 UTC
    Uhmmm, you're not giving the exact rules of the transformation, so I feel authorized to suggest Knuth's solution:
    print "A3:xyz45:M98:AB7:Z9"; # :)
    Update: added link.

    Flavio (perl -e "print(scalar(reverse('ti.xittelop@oivalf')))")

    Don't fool yourself.
Re: Best way to put : between fields in a string
by cog (Parson) on Apr 21, 2005 at 11:39 UTC
    Special variables to the rescue:

    Using $,

    my $s = 'A3xyz45M98AB7Z9'; local $, = ":"; print split /(?<=\d)(?=\D)/, $s;

    Using $"

    my $s = 'A3xyz45M98AB7Z9'; local $" = ":"; print "@{[split /(?<=\d)(?=\D)/, $s]}";
      Using $;

      my $s = 'A3xyz45M98AB7Z9'; my $i = 0; %_ = map { (++$i, $_) } split /(?<=\d)(?=\D)/, $s; local $; = ":"; $_{ (), map { delete $_{$_} } 1..$i }=0; print keys %_;

      Couldn't help myself O:-) Sorry :-)

Re: Best way to put : between fields in a string
by calin (Deacon) on Apr 21, 2005 at 12:28 UTC
    { local ($,, $\) = (":", "\n"); print unpack 'A2A5A3A3A2', 'A3xyz45M98AB7Z9'; }

    Works for me ;)

      I thought about doing it using fixed-length fields, but he seems to want to put a colon after each number followed by a letter (i.e. \d:[a-zA-Z]).

        Yeah, what did not surprise me was the fact that people jumped on to find "patterns" in the string etc. The question simply asked "Take string X and transform it into string Y". I have learned, as a life lesson, that second-guessing people is not always profitable, in fact it's not most of the times. Just give 'em what they want, with minimum complications. My post was intended for contrast.

        On a lighter side, I guess one could come up with an even more braindead solution (I'd investigate substr, index, for-a-la-C etc.)

Re: Best way to put : between fields in a string
by prasadbabu (Prior) on Apr 21, 2005 at 10:47 UTC

    TIMTOWTDI

    my $s = 'A3xyz45M98AB7Z9'; $s =~ s/[A-Za-z]+\d(?!$)/$&:/gsi; print "s='$s'"

    Prasad

      Minor correction:

      my $s = 'A3xyz45M98AB7Z9'; $s =~ s/([A-Za-z]+\d+)(?!$)/$1:/g; print "s='$s'\n"

Re: Best way to put : between fields in a string
by ambrus (Abbot) on Apr 21, 2005 at 12:45 UTC

    Here's one solution.

    local $_ = "A3xyz45M98AB7Z9"; s/(?<=\d)\B(?=\D)/:/g; print $_, $/;

    Update: I see this is the same as the solution of Roy Johnson.

Re: Best way to put : between fields in a string
by Tanktalus (Canon) on Apr 21, 2005 at 14:23 UTC

    To be honest, a description of what your cow-orker wants would be helpful, too. I prefer to write my solution in the language of the problem, rather than the language of the solution. It makes things more obvious later (and possibly, more robust when strange things happen).

    For example, if you were to say that "Each set of letters followed by set of numbers is a token, and we want colons to separate our tokens," then, IMO, splitting the string into said tokens, and joining with a colon, as you did, seems to be the best way to me to do it.

    But, if the requirement is, "I don't want numbers and letters to touch when the number is prior to the letter, so separate them with a colon," then I would suggest an alternate approach, such as s/(?<\d)(?>[[:alpha:]])/:/g.

    The point is to really pay attention to what you really need, and to write the solution that matches.

Re: Best way to put : between fields in a string
by piroufreek (Acolyte) on Apr 21, 2005 at 12:24 UTC
    One-liner:
    perl -e \ '$_="A3xyz45M98AB7Z9",s/(\d)([a-zA-Z])/$1:$2/gsi,print'
    Using split:
    use strict; my $s = "A3xyz45M98AB7Z9"; my @stuff = split(//,$s); for (my $j=0;$j<@stuff;$j++) { if ($stuff[$j] =~ /\d/ && $stuff[$j+1] && $stuff[$j+1] =~ /[a-zA-Z]/ +) { print "$stuff[$j]:"; } else { print "$stuff[$j]"; } }

    I'm working on a way to make it as complicated as possible. Just because.

    piroufreek

Re: Best way to put : between fields in a string
by wazoox (Prior) on Apr 22, 2005 at 11:24 UTC
    OK, let's try to make it as silly as possible:
    my $s = 'A3xyz45M98AB7Z9'; ($t=join'',map{/\d+/g?$_.=':':$_}split//,$s)=~s/:(\d+|$)/$1/g; print $t;
    Now I'm looking for an even worse way to achieve it :)