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

Hi monks,

I'm trying to have a regex to change the filenames on the left to the ones on the right:

1) mypic.jpg.jpg => mypic_jpg.jpg
2) mypic.jpg.jpg.jpg => mypic_jpg_jpg.jpg
2) fish.fish.gif => fish_fish.gif

Basically, I want to replace every dot but the last with an underscore.

I haven't had much luck yet so I don't have any code to show.

Could someone enlighten me?

Thanks in anticipation :)

Update: Thanks to all for your help :)

Replies are listed 'Best First'.
•Re: Replace all occurrences but the last
by merlyn (Sage) on Aug 22, 2004 at 15:24 UTC
      Thanks, merlyn!

      I was trying with the positive lookahead thing but I just didn't get it.

Re: Replace all occurrences but the last
by ysth (Canon) on Aug 22, 2004 at 15:51 UTC
    substr($s, 0, rindex($s, ".")) =~ y/./_/;
Re: Replace all occurrences but the last
by Aristotle (Chancellor) on Aug 22, 2004 at 18:35 UTC
    1 while s/ \. ( .* \. ) /_$1/x;

    You can also make that non-greedy, it doesn't matter.

    Makeshifts last the longest.

Re: Replace all occurrences but the last
by ambrus (Abbot) on Aug 22, 2004 at 15:31 UTC

    One solution is to split the string at the last dot, then change the dots to underscores everywhere before it.

    $_ = "mypic.jpg.jpg"; if (/^(.*)(\..*)/s) { my($p, $s) = ($1, $2); $p=~y/./_/; $_ = $p.$s; } print $_, $/;

    The if is needed so that you don't get an error if the string does not have a dot in it.

    Update: Ysth's reply has reminded me that you can bind substitutions to substr. That's indeed a simpler solution than what I've given above.

    Thus, here's a new variant using the same feature:

    $_ = "mypic.jpg.jpg"; /^(.*)\./s and substr($_, 0, $+[1])=~s/\./_/g; print $_, $/;

    Update 2: Added hat anchor before (.*). Is that good?

    Update 3: I mean Why does a Perl 5.6 regex run a lot slower on Perl 5.8?

Re: Replace all occurrences but the last
by ambrus (Abbot) on Aug 22, 2004 at 16:31 UTC

    And now let's see a bit more trickier solution.

    $_ = "fish.fish.gif"; s/\./\.\./g; s/^(.*)\./$1/s; s/\.\./_/g; print $_, $/;
Re: Replace all occurrences but the last
by Anonymous Monk on Aug 23, 2004 at 11:03 UTC

      Silly rabbit. :-) Nice approach, though.

      # legible: join( "_", split( /\./, $_, y/.// ) );

      The tr/// here counts the dots in the string and uses the number as a limit to split — since the number is one less than the number of fragments delimited by dots, the last dot in the string does not get to serve as a delimiter.

      Makeshifts last the longest.