multiple text replacements from a hash

G

George Mpouras

Hello,

I have a hash holding "from" - "to" text pieces.
I want to replace on a given text all the "from" parts with the "to".
The following piece of code is working, but I know it is doing not necessary
loops, can you help me to do it better ;



use strict;
use warnings;
use feature qw/say/;

my $text = '172.16.0.36 customer1.com company.comcompany.com erw
172.16.0.36customer1.com';

my %Replace = (
'10.1.1.21' => '192.168.0.9',
'255.255.0.0' => '255.255.255.0',
'customer1.com' => 'local.lan',
'172.16.0.1' => '192.168.0.1',
'company.com' => 'local.lan',
'10.1.31.100' => '192.168.0.7',
'172.16.0.36' => '192.168.0.7',
'10.1.18.105' => '192.168.0.1',
'255.255.240.0' => '255.255.255.0',
'172.16.0.2' => '192.168.0.9');





# We find the min and max hash lengths to avoid later as many iterations is
possible
my $Min_length;
my $Max_length = 0;
foreach my $property (keys %Replace) {
$Max_length = length $Replace{$property} if length $Replace{$property} >
$Max_length;
$Min_length //= $Max_length;
$Min_length = length $Replace{$property} if length $Replace{$property} <
$Min_length;
}



my $offset = 0;
while ( $offset < length $text) {
my $offset_walk = 0;
#say "*$offset* $text";

for (my $i = $Max_length; $i >= $Min_length; $i--) {
my $piece = substr($text, $offset, $i);

if ( exists $Replace{$piece} ) {
substr($text, $offset, $i, $Replace{$piece});
$offset_walk = length $Replace{$piece};
last
}
}

$offset += $offset_walk == 0 ? 1 : $offset_walk;
}


say $text;
 
R

Rainer Weikusat

I have a hash holding "from" - "to" text pieces.
I want to replace on a given text all the "from" parts with the
"to".

my $text = '172.16.0.36 customer1.com company.comcompany.com
erw 172.16.0.36customer1.com';

my %Replace = (
'10.1.1.21' => '192.168.0.9',
'255.255.0.0' => '255.255.255.0',
'customer1.com' => 'local.lan',
'172.16.0.1' => '192.168.0.1',
'company.com' => 'local.lan',
'10.1.31.100' => '192.168.0.7',
'172.16.0.36' => '192.168.0.7',
'10.1.18.105' => '192.168.0.1',
'255.255.240.0' => '255.255.255.0',
'172.16.0.2' => '192.168.0.9');

Considering that none of your keys is a prefix of another key, the
obvious idea would be to collect them all into a regex:

my $re = join('|', map { quotemeta($_) } keys(%Replace));
$re = qr/($re)/;

and do all replacements with the single s/// statement

$text =~ s/$re/$Replace{$+}/g;

If you had keys which are prefixes of other key, you could use

my $re = join('|', map { quotemeta($_) } sort { $b cmp $a } keys(%Replace));

to prefer the longer match or

my $re = join('|', map { quotemeta($_) } sort keys(%Replace));

to prefer the shorter.

Another good idea might be to use an existing macro (pre-)processor,
eg, m4, for tasks like this.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,014
Latest member
BiancaFix3

Latest Threads

Top