tags:

views:

157

answers:

4

I'd like to do something like this:

my $text = "The owls are not what they seem.";  
my $pattern = '(\s+)';  
my $replacement = '-$1-';  
$text =~ s/$pattern/$replacement/g;

$text should then be: The- -owls- -are- -not- -what- -they- -seem.

But of course it's more like: The-$1-owls-$1-are-$1-not-$1-what-$1-they-$1-seem.

I tried all kinds of backreferences ($1, \1, \g{1}, \g1) and they all didn't work. The /e modifier didn't work either. Is this possible at all?

The purpose is to alter some text inside an object with a line like this: $object->replace('(.)oo', '$1ar')

Any other ideas how this could be done?

Thank you very much.

A: 

Double quote your replacement string instead of single quoting it. That should allow the placeholder to be interpolated correctly.

Aquatoad
This doesn't do the trick.
Andy Lester
That puts the value of $1 inside $replacement. But I want a literal '$1' in there, which would be replaced with the corresponding backreference later.
wuwuwu
A: 

$text =~ s/$pattern/-$1-/g;

Andy Lester
No, I can't provide the '-$1-' part directly like this. It's inside a variable: $replacement = '-$1-';$text =~ s/$pattern/$replacement/g;
wuwuwu
+10  A: 

You could eval and then expand strings using /ee:

my $text = "The owls are not what they seem.";
my $pattern = '(\s+)';
my $replacement = q{"-$1-"};
$text =~ s/$pattern/$replacement/eeg;

From perldoc perlop:

e Evaluate the right side as an expression.

ee Evaluate the right side as a string then eval the result

However, I would feel safer with

my $replacement = sub { "-$1-" };
$text =~ s/$pattern/$replacement->()/eg;

But it all depends on the context in which you are doing this.

Sinan Ünür
ee = Eval then expand variables.
James van Dyke
I could have thought of the sub. It's so obvious now. And it fits perfectly. Thank you!
wuwuwu
+1  A: 

Sinan Ünür's solution will work but it still requires the replacement string to be a literal inside the program at some point. If that replacement string comes from data, you'll have to do something a little fancier:

sub dyn_replace {
  my ($replace) = @_;
  my @groups;
  {
    no strict 'refs';
    $groups[$_] = $$_ for 1 .. $#-;      # the size of @- tells us the number of capturing groups
  }
  $replace =~ s/\$(\d+)/$groups[$1]/g;
  return $replace;
}

and then use it like

$text =~ s/$pattern/dyn_replace($replacement)/eg;

Note that this also avoids eval and allows the use of modifiers like /g. Code taken from this Perl Monks node but I wrote that node so it's ok :)

Dan
As far as I understand this it's somehow the same as Sinan Ünür's subref solution, except it's much more complicated.
wuwuwu
Sinan's solution requires $replacement to be a literal string inside the subref. Mine allows it to be any arbitrary string value.
Dan