Valentin P.
Valentin P.

Reputation: 1151

replace part of file path via regex

I try to replace directory part of file fullname in Perl. Something like: got filename 'D:\Texts1\text1' in directory 'D:\Texts1', want to replace it with 'D:\Texts2' and then filename will be 'D:\Texts2\text1'.

I try this code:

$filename = 'D:\Texts1\text1';
$i = 'D:\Texts1';
$o = 'D:\Texts2';
$filename =~ s'$i'$o'g;

And it does not take effect. $filename doesn't changes. I tried to use something like

$i = quotemeta('D:\Texts1');

but it also has not took effect.

Upvotes: 1

Views: 2825

Answers (6)

Baiyan Huang
Baiyan Huang

Reputation: 6771

There are several valid answers here, I would like to compile a comprehensive answer along with mine to make this post easier to read:

Root Cause

$i = 'D:\Texts1';

when used as a regex pattern, "\" should be escaped - what the regex engine want is some ultimate format like: D:\\Texts1. So this doesn't work, however, there are at least 4 different ways to build this format as listed below.

Also to notice, when ' is used as delimiter for match or substitution statement, variable interpolation will be disabled, which renders $filename =~ s'$i'$o'g; almost useless. so first step, change it to use / or {}

Solution 1

use quotemeta, this will effectively escape the "\":

$filename = 'D:\Texts1\text1';
$i = quotemeta('D:\Texts1');
$o = 'D:\Texts2';
$filename =~ s/$i/$o/g;

Solution 2

use \Q .. \E, which has similar effects as quotemeta:

$filename = 'D:\Texts1\text1';
$i = 'D:\Texts1';
$o = 'D:\Texts2';
$filename =~ s/\Q$i\E/$o/g; # or s/\Q$i/$o/g also works

Solution 3

escape the "\" in the string explicitly, and use qr to quote the string as regex pattern.

$filename = 'D:\Texts1\text1';
$i = qr 'D:\\Texts1';
$o = 'D:\Texts2';
$filename =~ s/$i/$o/g;

Solution 4

escape to the extent that the string is ready for regex:

$filename = 'D:\Texts1\text1';
$i = 'D:\\\\Texts1';
$o = 'D:\Texts2';
$filename =~ s/$i/$o/g;

Upvotes: 2

John Corbett
John Corbett

Reputation: 1615

this doesn't interpolate

$filename =~ s'$i'$o'g;

try using / instead of ', like this:

$filename =~ s/$i/$o/g;

that should work. ' prevents string interpolation, so the variable names appear as string literals. Also, make sure to use the quotemeta like you were doing before.

Upvotes: 1

dan1111
dan1111

Reputation: 6566

In fact you are experiencing a combination of two problems:

  • Single quotes, while valid as a regexp delimiter, have a special meaning: they disable variable interpolation. Thus you are searching your string for the literal pattern $i (if you have warnings enabled, you get a clue to this--it tells you that the variables $i and $o are only used once in your program).
  • As others have pointed out, you also need the \Q...\E construct or quotemeta() in order to avoid interpreting the special characters in your variable as regexp operators.

Upvotes: 0

Alex Yaroshevich
Alex Yaroshevich

Reputation: 78

Updated code

use strict;
use warnings qw/all/;

my $filename = 'D:\Texts1\text1';
my $i = 'D:\\Texts1';
my $o = 'D:\\Texts2';
$filename =~ s/\Q$i\E/$o/;

print $filename;

Be happy!

Upvotes: -1

shi
shi

Reputation: 131

you need to add \Q:

$filename =~s{\Q$i}{$o};

Upvotes: 0

Alan Haggai Alavi
Alan Haggai Alavi

Reputation: 74202

The \ in D:\Texts1 is the problem. You need to escape this metacharacter. For this purpose, the string should be wrapped by \Q and \E.

$filename =~ s/\Q$i\E/$o/g;

Upvotes: 1

Related Questions