brwarn has asked for the wisdom of the Perl Monks concerning the following question:

Hello Monks, I can't get a backreference to work properly. It is being replaced by my script as "$1" rather than the capturing group I want to be substituted instead. What (boneheaded no doubt) thing am I doing wrong? regex.txt contains the line:
([A-Za-z])\.[A-Za-z],$1
my $file = "regex.txt"; open FILE, "<", $file or die "Can't open $file: $!\n"; while(my $regex=<FILE>){ my @rex = split(/,/,$regex); chomp($rex[0]); # $rex[0] is set to "([A-Za-z])\.[A-Za-z]" chomp($rex[1]); # $rex[1] is set to "$1" <--not sure how to ge +t ([A-Za-z])group from $rex[0] to be set here. $rrow_test =~ s/$rex[0]/$rex[1]/ig; } # END while(my $regex=<FILE>) close FILE;
If I replace $1 in regex.txt with \1, I get the error:
Reference to nonexistent group in regex; marked by <-- HERE in m/\1 <- +- HERE / at C:\utilities\regexTester.pl line 105, <FILE> line 2.
Thanks.

Replies are listed 'Best First'.
Re: backreference replaced as literal or just fails
by GrandFather (Saint) on Mar 10, 2009 at 19:36 UTC

    You need to doubly evaluate the substitution string:

    use strict; use warnings; my $fileStr = <<'END_FILE'; ([A-Za-z])\.[A-Za-z],$1 END_FILE open my $inFile, "<", \$fileStr or die "Can't open file: $!\n"; my $rrow_test = 'A.BC'; while (my $regex = <$inFile>) { chomp $regex; my @rex = split (/,/, $regex); $rrow_test =~ s/$rex[0]/$rex[1]/eeg; print "$rrow_test\n"; } close $inFile;

    Prints:

    AC

    Note that double evaluating the string is like using string eval and is generally considered evil, or at least a huge security risk. For example, try it with the string '([A-Za-z])\.[A-Za-z],@{[print "this could have been: `rm *`\n"]}' in $fileStr


    True laziness is hard work
      Thank you everyone for your assistance!
Re: backreference replaced as literal or just fails
by kennethk (Abbot) on Mar 10, 2009 at 19:04 UTC
    Please read Writeup Formatting Tips and How (Not) To Ask A Question. In particular, wrap your input file in code tags, since it is getting misformatted. Also, you are binding $rrow_test to your substitution, but don't give us a value. You probably want to reformat your open statement to open FILE, "<", $file or die "Can't open $file: $!\n"; as well, but that's more subjective.
      Sorry for missing the input part. With respect to your binding point, I must not understand the usage. I understand
      $rrow_test =~ s/$rex[0]/$rex[1]/ig;
      to mean
      "Replace the first occurrence of $rex[0] with $rex[1]."
      I'm setting the $rex array values on the 'split' line of the code. Am I understanding your point/question correctly or have I missed the mark altogether? Thanks.
        The "binding" issue is that you didn't give us any possible values for $rrow_test, thus making it harder to determine intent. In any case, the issue you are likely encountering is that you expect your strings to get interpolated twice. According to Regexp Quote-Like Operators, variables you pass get interpolated at run time. This means that "$rex[1]" gets interpolated to "$1", the literal value (Update: as GrandFather points out below). Your solution is to doubly interpolate the string, using the ee modifier. Note this is potentially a serious security risk.

        Also note your i modifier is useless in your current construct, as you already handle both upper- and lower-case letter.

        "Replace the first occurrence of $rex[0] with $rex1."

        Actually /g means replace all occurrences.