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?