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

Hello monks!
I have a string that I need to make changes to:
Consider:
$string='III...MMMMMOOOO....MMMIIIII';
What I must is:
III...MMMMMOOOO --> IIIIIIMMMMMOOOO OOOO....MMMIIIII --> OOOOOOOOMMMIIIII
this can be done with ~s in a reg exp? Because I tried:
$string=~s/\.+M+i+/oMi/g;
and it printed:
III...MMMMMOOOOoMi
Apparently i'm doing something wrong...

Replies are listed 'Best First'.
Re: How to make these reg exp changes?
by keszler (Priest) on Nov 15, 2009 at 22:52 UTC
    Here's one method:
    perl -e "$s='OOO....MMMM..IIIII';$s=~s/(.)(\.+)/$1.$1 x length($2)/eg; +print $s,$/;" # OOOOOOOMMMMMMIIIII

    Or more appropriately for here:

    perl -e "$s='JJ..A....PPPP..HHHH...';$s=~s/(.)(\.+)/$1.$1 x length($2) +/eg;print $s,$/;" # JJJJAAAAAPPPPPPHHHHHHH
Re: How to make these reg exp changes?
by ikegami (Patriarch) on Nov 15, 2009 at 23:17 UTC
    s/(.)(\.+)/$1 x (1+length $2)/seg;

    For example,

    $ perl -wle' $_ = "III...MMMMMOOOO....MMMIIIII"; print; s/(.)(\.+)/$1 x (1+length $2)/seg; print; ' III...MMMMMOOOO....MMMIIIII IIIIIIMMMMMOOOOOOOOMMMIIIII

    Update: Oops, this window must have been open a while. keszler already gave basically the same answer a while ago. As punishment, I'll give an alternative that might be faster for short strings:

    $ perl -wle' $_ = "III...MMMMMOOOO....MMMIIIII"; print; 1 while s/(.)(\.)/$1$1/sg; print; ' III...MMMMMOOOO....MMMIIIII IIIIIIMMMMMOOOOOOOOMMMIIIII
Re: How to make these reg exp changes?
by Anonymous Monk on Nov 15, 2009 at 22:43 UTC
    For the first substitution case, I tried:
    if($string=~/(.*?)(\.+)(M+i+)/i) { $first=$1; $change=$2; $len_change=length($change); $third=$3; print $first; print 'O' x $len_change; print $third; print "\n"; }
    and it printed:
    III...MMMMMOOOOOOOOMMMiiiii
    which is correct. Is there a quicker way however?
      But the thing is that although I change ...MMMiii to OOOMMMiii , at the same time, I must change III...MMMOOO to IIIMMMOOOO which is not covered by the previous reg exp...
      any suggestions?
Re: How to make these reg exp changes?
by Anonymous Monk on Nov 15, 2009 at 23:26 UTC
    Your solutions work OK - thanks to both of you!
    But I have a question (maybe I should have written this before):
    What I must change is:
    ....MMMMiiii => ooooMMMMiiii ....MMMMoooo => iiiiMMMMoooo iiiiMMMM.... => iiiiMMMMoooo ooooMMMM.... => ooooMMMMiiii
    I mean, If you do not have some character before the '.' , how will it understand to replace for example, '.' with 'I' if I have 'O' after the string of 'M'? Is there any chance that this can be done?
      $ perl -wle' my %neg = ( o => "i", i => "o" ); while (<>) { chomp; s/([io])(M*)(\.+)/ $1.$2.( $neg{$1} x length($3) ) /eg; s/(\.+)(?=M*([io]))/ $neg{$2} x length($1) /eg; print; } ' ....MMMMiiii ....MMMMoooo iiiiMMMM.... ooooMMMM.... ooooMMMMiiii iiiiMMMMoooo iiiiMMMMoooo ooooMMMMiiii

      Simpler version that only works with Perl 5.10+:

      $ perl -wle' my %neg = ( o => "i", i => "o" ); while (<>) { chomp; s/([io])M*\K(\.+)/ $neg{$1} x length($2) /eg; s/(\.+)(?=M*([io]))/ $neg{$2} x length($1) /eg; print; } ' ....MMMMiiii ....MMMMoooo iiiiMMMM.... ooooMMMM.... ooooMMMMiiii iiiiMMMMoooo iiiiMMMMoooo ooooMMMMiiii

      I could have used -p above, but I wanted to make clear that %neg only needs to be initialized once without complicating the issue by adding a BEGIN. Here's the 5.10+ version using -p:

      perl -ple' BEGIN { %neg = qw( i o o i ) } s/([io])M*\K(\.+)/ $neg{$1} x length($2) /eg; s/(\.+)(?=M*([io]))/ $neg{$2} x length($1) /eg; '

      Update: Added \K version. \K rocks.
      Update: Added explanation about -p.

        exactly what I was looking for... The trick with the hash was just the thing!
        Thank you very much!