Perl: use referencias inversas en una variable de cadena de reemplazo

Aug 21 2020

Estoy realizando una sustitución de cadenas en Perl, pero tengo tanto el patrón como las cadenas de reemplazo almacenadas como variables escalares fuera de los operadores de expresiones regulares. El problema es que quiero que la cadena de reemplazo pueda usar referencias inversas.

Espero que el siguiente código ilustre el asunto más claramente.

my $pattern = 'I have a pet (\w+).';
my $replacement = 'My pet $1 is a good boy.';
my $original_string = 'I have a pet dog.';

# Not Working
my $new_string = $original_string =~ s/$pattern/$replacement/r;

# Working
#my $new_string = $original_string =~ s/$pattern/My pet $1 is a good boy./r;

# Expected: "My pet dog is a good boy."
# Actual: "My pet $1 is a good boy."
print "$new_string\n";

Respuestas

5 ikegami Aug 21 2020 at 12:23
s/$pattern/My pet $1 is a good boy./

es corto para

s/$pattern/ "My pet $1 is a good boy." /e

La expresión de reemplazo ( "My pet $1 is a good boy.") es un literal de cadena que interpola $1.


Esto significa que

s/$pattern/$replacement/

es corto para

s/$pattern/ "$replacement" /e

La expresión de reemplazo ( "$replacement") es un literal de cadena que interpola $replacement(no $1).


Si bien puede ser un obstáculo para usted, es bueno que perlno tenga la costumbre de ejecutar el contenido de las variables como código Perl. :)

Puede usar gsub_copydesde String::Substitution para resolver su problema.

use String::Subtitution qw( gsub_copy );

my $pattern         = 'I have a pet (\w+)\.';
my $replacement     = 'My pet $1 is a good boy.';
my $original_string = 'I have a pet dog.';

my $new_string = gsub_copy($original_string, $pattern, $replacement);
4 zdim Aug 21 2020 at 09:54

Eso $1en la cadena de reemplazo es solo caracteres sucesivos $y 1, y para convertirlo en una variable para la primera captura, tendría que pasar por malos aros.

¿Qué tal una alternativa?

my string = q(a pet dog);

my $pattern = qr/a pet (\w+)/;

my $new = $string =~ s/$pattern/ repl($1) /er;


sub repl {
    my ($capture) = @_;
    return "$capture is a good boy";
}

donde el submarino es realmente solo

sub repl { "$_[0] is a good boy" }

Es un poco más, pero luego es más capaz y flexible.


O, como resulta según la respuesta de ikegami, use String::Substitution que envuelve todas las 'sutilezas' involucradas en una sola llamada