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

Hi,

I am trying to take reasonable search and replace expressions and use them within perl like what you "think" the result would be for =~ s/$find/$replace/ if perl were an AI. I saw some posts, but none that passed strict/warning, where readable by a normal human like me, or worked in the general case where $1 might be after $2 etc. in the replacement.

But I cannot seem to get the sprintf to work. I have tried many variataions, but am missing something in my understanding of the interpolation/interaction within the replace. Trying to have the sprintf create the proper "string" for proper singe-level interpolation by the replace so that the $\d have an actual meaningful value

Script: (hard-coded works, half hard-coded and full variable substitution have different issues)

use strict; use warnings; # EX: Change '.' 'char1' 'char2' to 'b' 'char2' 'char1' 's' my $input_string = 'foo.ra'; my $find = '\.(.)(.)$'; my $replace = 'b$2$1s'; my $def_value0 = stringExpressionReplace($input_string,$find,$replace, +0); my $def_value1 = stringExpressionReplace($input_string,$find,$replace, +1); my $def_value2 = stringExpressionReplace($input_string,$find,$replace, +2); # Use varaible-based substitutions in search/replace pattern sub stringExpressionReplace { my $mod_string = $_[0]; my $find = $_[1]; my $replace = $_[2]; my $sr_type = $_[3]; print "\nstringExpressionReplace String: $mod_string Find: $find Rep +lace: $replace\n"; my $orig_string = $mod_string; my $orig_replace = $replace; $replace = "\"" . $replace . "\""; if($sr_type == 1) { # Reorder issue #$mod_string =~ s/$find/sprintf($replace,$1,$2,$3,$4,$5)/e; # Exam +ple, ordering issue print "stringExpressionReplace sr_type = 1 Replace var with substi +tution hard-coded.\n"; $mod_string =~ s/$find/sprintf($replace,$2,$1)/e; # Hard-coded ord +er, var list not a string print "stringExpressionReplace SR command: s/$find/sprintf($replac +e,\$2,\$1)/e\n"; print "stringExpressionReplace String: $orig_string --> $mod_strin +g Find: $find Replace: $replace\n" if($orig_string ne $mod_st +ring); } elsif($sr_type == 2) { my @replacements = ( $replace =~ /(\$\d)/g ); # Store Replacement + list in order my $replace_count = scalar @replacements; $replace =~ s/\$\d/\%s/g; print "stringExpressionReplace Format String: $orig_replace --> $r +eplace\n"; my $var_list = ""; if(@replacements) { foreach my $var (@replacements) { $var_list .= "$var,"; } $var_list =~ s/,$//; print "stringExpressionReplace Var String: $var_list\n"; } if(@replacements) { # perl expression $-based subsitution found print "stringExpressionReplace sr_type = 2 Replace and var lists + as variables.\n"; print "stringExpressionReplace SR command: s/$find/sprintf($repl +ace,$var_list)/e\n"; $mod_string =~ s/$find/sprintf($replace,$var_list)/e; #$mod_string =~ s/\.(.)(.)$/sprintf("b%s%ss",$2,$1)/e; print "stringExpressionReplace String: $orig_string --> $mod_str +ing Count $replace_count Find: $find Replace: $orig_replace --> $repl +ace $var_list\n"; } else { $mod_string =~ s/$find/$replace/; print "stringExpressionReplace String: $orig_ +string --> $mod_string\n" if($orig_string ne $mod_string); } } else { print "stringExpressionReplace completely hard-coded.\n"; $mod_string =~ s/\.(.)(.)$/b$2$1s/; print "stringExpressionReplace sr_type = 0 String: $orig_st +ring --> $mod_string\n" if($orig_string ne $mod_string); } return $mod_string; }

#Ouptut:

stringExpressionReplace String: foo.ra Find: \.(.)(.)$ Replace: b$2$1s

stringExpressionReplace completely hard-coded.

stringExpressionReplace sr_type = 0 String: foo.ra --> foobars

stringExpressionReplace String: foo.ra Find: \.(.)(.)$ Replace: b$2$1s

stringExpressionReplace sr_type = 1 Replace var with substitution hard-coded.

Redundant argument in sprintf at sr_ex.pl line 29.

stringExpressionReplace SR command: s/\.(.)(.)$/sprintf("b$2$1s",$2,$1)/e

stringExpressionReplace String: foo.ra --> foo"b$2$1s" Find: \.(.)(.)$ Replace: "b$2$1s"

stringExpressionReplace String: foo.ra Find: \.(.)(.)$ Replace: b$2$1s

stringExpressionReplace Format String: b$2$1s --> "b%s%ss"

stringExpressionReplace Var String: $2,$1

stringExpressionReplace sr_type = 2 Replace and var lists as variables.

stringExpressionReplace SR command: s/\.(.)(.)$/sprintf("b%s%ss",$2,$1)/e

Missing argument in sprintf at sr_ex.pl line 49.

stringExpressionReplace String: foo.ra --> foo"b$2,$1s" Count 2 Find: \.(.)(.)$ Replace: b$2$1s --> "b%s%ss" $2,$1

Thanks!

  • Comment on sprintf using variables for format and variables to do a search and replace, sprintf issues
  • Download Code

Replies are listed 'Best First'.
Re: sprintf using variables for format and variables to do a search and replace, sprintf issues
by hv (Prior) on Mar 07, 2023 at 01:06 UTC

    If I correctly understand the question, you don't need sprintf for this at all, but in the substitution you need a double eval, and a corresponding double layer of quoting:

    % cat t0 use strict; use warnings; my $input = 'foo.ra'; my $find = '\.(.)(.)$'; my $replace = 'b$2$1s'; printf qq{result "%s"\n}, replace($input, $find, $replace); exit 0; sub replace { my($input, $find, $replace) = @_; $input =~ s{$find}{qq{"$replace"}}ee or die "input did not match"; return $input } % perl t0 result "foobars" %

    The first eval creates the string "b$2$1s", the second eval then works out that that should be "bars".

    At a brief glance your (type 2) sprintf solution could probably also be made to work, but would also need a double eval and will get rather complicated. So I would not recommend pursuing that approach further other than as a learning experience.

Re: sprintf using variables for format and variables to do a search and replace, sprintf issues
by Bod (Parson) on Mar 07, 2023 at 00:40 UTC

    From the block of code, lack of vertical spacing and unclear question, I think this is one line you are asking about...

    $mod_string =~ s/$find/sprintf($replace,$var_list)/e;

    I would start by isolating just that line of code and printing out $find and $replace to ensure they contain what you think they do.

    Looking at your code it appears $replace is 'b$2$1s'. This has backreferences so $find has to contain capture groups. If this is not the case you will get "Not enough arguments for sprintf".

Re: sprintf using variables for format and variables to do a search and replace, sprintf issues
by Marshall (Canon) on Mar 07, 2023 at 10:55 UTC
    I think I understand what you are trying to do although your question wasn't clear to me at the outset.

    First, this /ee stuff is messy and I read this post StackOverflow re: variable in replacement side which talks about some of the details.
    Then I came across: String::Substitution module.
    I figured that this module does exactly what you want, so I installed and tested it.

    use strict; use warnings; use String::Substitution qw(gsub_copy); my $in_string = 'foo.ra'; my $find_exp = '\.(.)(.)$'; my $replace_exp = 'b$2$1s'; my $output = gsub_copy($in_string, $find_exp, $replace_exp); print "$output\n"; # prints: foobars
    You can read thru the docs and see more options.

      Thanks for the reply. The sprintf was a bit interesting as I had to isolate the to do ${1} etc. as the variables were sometimes followed by a number say $145 and I really needed the $1 ... I will look over the gsub_copy as it might be better as my "solution" is limited to 9 matches currently, which should me more than enough for the purpose of this script, which should need no more than 2. This is what I ended up with last night:

      sub stringExpressionReplace { my $mod_string = $_[0]; my $find = $_[1]; my $replace = $_[2]; my $orig_string = $mod_string; my $mod_replace = sprintf("\"%s\"",$replace); $mod_replace =~ s/\$(\d)/\${$1}/g; $mod_string =~ s/$find/$mod_replace/ee; print " stringExpressionReplace String: $orig_string + --> $mod_string\n" if($orig_string ne $mod_string); return $mod_string; }