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

Hello, i was pondering quite a while over the question if
s/\Q$find\E/$replace/g
really does a plain string replace, because the replace part has meta symbols as well (e.g. \1) and these are not quoted. And what happens if $find happens to contain \E? Is using quotemeta safer than using \Q and \E? And is there something like quotemeta for the replace part?

Or is there a simple function that does a simple string replace? Sure i can easily write one, but i am sure there is a simple way of doing this, that doesn't give me bad dreams of failing perl scripts in the night, and is still elegant.

Michael

Replies are listed 'Best First'.
Re: Does s/\Q$find\E/$replace/g really a plain string replace ?
by ikegami (Patriarch) on Oct 02, 2004 at 16:05 UTC

    Sometimes, it's just easier to try it out:

    $_ = 'foo bah bar'; $find = 'bah'; $replace = '[\1]'; s/(\Q$find\E)/$replace/g; $\ = "\n"; print; # "foo [\1] bar" and not "foo [bah] bar"

    But there is indeed a (more efficient?) way:

    $_ = 'foo bah bar'; $find = 'bah'; $replace = '[\1]'; $idx = index($_, $find); substr($_, $idx, length($find), $replace) if $idx >= 0; $\ = "\n"; print;

    Update: Here's a version that does mutliple matches:

    $_ = 'foo bah bah bar'; $find = 'bah'; $replace = '[\1]'; $idx = length($_); while (($idx = rindex($_, $find, $idx-1)) >= 0) { substr($_, $idx, length($find), $replace); } $\ = "\n"; print;

      Good job.

      You forgot to accomodate the /g on the regexp. I'd prefer to write this as a s///g but here's your version, reconstituted for the /g feature.

      while ( ( my $index = index substr( $_, pos() ), $find), $index != -1 ) { substr $_, $index, length $find, $replace; pos() = $index + length $find; }
      while ( ( my $index = index $_, $find), $index != -1 ) { substr $_, $index, length $find, $replace; }

        At the same time you were writting that, I was writting my own /g! Mine doesn't have an infinite loop like yours does, however. Try yours with:

        $_ = 'foo bah bar'; $find = 'bah'; $replace = '[bah]'; first time in loop: foo [bah] bar second time in loop: foo [[bah]] bar third time in loop: foo [[[bah]]] bar ...
      $_ = 'foo bbbbbbbah bar'; $find = 'bah'; $replace = 'ah';

      and

      $_ = 'foo babab bar'; $find = 'bab'; $replace = 'xxx';

      don't work very well.

      Perhaps something like:

      sub replace { die "Some helpful messsage" unless 3 == grep defined $_, @_[0..2]; my ($str,$find,$rep) = @_; my $i = 0; my $l = length($find); my @pos; while ($i < length($str)) { $i = index($str,$find,$i); last if $i < 0; push @pos, $i; $i += $l; } substr($str,$_,$l,$rep) for reverse @pos; return $str; }
Re: Does s/\Q$find\E/$replace/g really a plain string replace ?
by !1 (Hermit) on Oct 02, 2004 at 21:51 UTC
    And what happens if $find happens to contain \E?
    #!/usr/bin/perl -l $_ = "\\E"; print; print qr(\Q$_\E); __END__ \E (?-xism:\\E)

    Looks as if it does what it should.