How to replace a set of search/replace pairs?

perl

Solution

First, your hash initialization is off: A hash is initialized as a list:

my %dict = ( hello => 'hola', goodbye => 'adios' , ... );

Or you can use a hash reference:

my $dict = { hello => 'hola', goodbye => 'adios' , ... };

which is a scalar.

Replacing the keys with the values in a string is easy:

s/$_/$dict{$_}/g for keys %dict;

unless

- The contents of substitutions shall not be replaced, e.g. `%dict = (a => b, b => c)` should transform `"ab"` to `"bc"` (not to `"cc"` as the above solution may or may not do, hash order is random).

- The keys can contain regex metacharacters like `.`, `+`, or `()`. This can be circumvented by escaping regex metacharacters with the `quotemeta` function.

The traditional approach is to build a regex that matches all keys:

my $keys_regex = join '|', map quotemeta, keys %dict;

Then:

$string =~ s/($keys_regex)/$dict{$1}/g;

which solves all these issues.

In the regex building code, we first escape all keys with `map quotemeta`, and then join the strings with `|` to build the regex that matches all keys. The resulting regex is quite efficient.

This guarantees that each part of the string is only translated once.

Problem

I have a dictionary of translations as a hash: ``` my %dict = { hello => 'hola', goodbye => 'adios' , ... } ``` (The actual use-case is not a human language translation! I'm replacing a load of tokens with some other values. This is just for example.) How can I apply each of these to a string? Obviously I could loop them and pass each to `s/$key/$value/` but then I'd have to quote them so it wouldn't break if a search or replacement had (for example) `/` in it. In PHP there's `strtr($subject, $replacement_pairs_array)` - is there anything similar in Perl?

Original source