search/replace values from an array - Part 2.

S

Sandman

Hello again, and thanks to Anno Siegel for your answer to my earlier thread
(on the subject, but
unfortunately, the conditions for my need has slightly altered, and I want some
further help (from anyone, not just Anno).


Here is a code snippet that probably illustrates what I want to do:

-------------
#!/usr/bin/perl
use strict;
use warnings;

my $string = "Hello World!";

my %array = (
"Hello (.*?)!", "Goodbye, little #1#!"
# search reply
);
foreach (keys %array){
if ($string=~m/$_/i){
# It matched!

$array{$_}=~s/#(\d+)#/$$1/;
# replace #1# with $1, #2# with $2
# from the match above

print "$string\n\n";
print "$array{$_}\n\n";
# Add the reply to the string
}
}
-------------

Wanted output:

Hello World!

Goodbye, little World!

So, as opposed to my earlier thread, I no longer seek to replace the original
string, but rather add a "reply" to it, if it matches. Basically, what I want
to do is replace the string #1#, #2#, #3#... with the values in $1, $2, $3...
but when trying to do that, perl complains about:

File "test.pl"; Line 15: Can't use string ("1") as a SCALAR ref while
"strict refs" in use

Line 15 being:

$array{$_}=~s/#(\d+)#/$$1/;

Thanks for any help.
 
A

Anno Siegel

Sandman said:
Hello again, and thanks to Anno Siegel for your answer to my earlier thread
(on the subject, but
unfortunately, the conditions for my need has slightly altered, and I want some
further help (from anyone, not just Anno).


Here is a code snippet that probably illustrates what I want to do:

-------------
#!/usr/bin/perl
use strict;
use warnings;

my $string = "Hello World!";

my %array = (
"Hello (.*?)!", "Goodbye, little #1#!"
# search reply
);
foreach (keys %array){
if ($string=~m/$_/i){
# It matched!

$array{$_}=~s/#(\d+)#/$$1/;

Well, this doesn't do what you hope it does. You knew that, and I
showed you a way to do the replacements. Why are you trying it again?
# replace #1# with $1, #2# with $2
# from the match above

print "$string\n\n";
print "$array{$_}\n\n";
# Add the reply to the string

What does that mean? Append it?
}
}
-------------

Wanted output:

Hello World!

Goodbye, little World!

So, as opposed to my earlier thread, I no longer seek to replace the original
string, but rather add a "reply" to it, if it matches.

Trivial. Copy the original, modify the copy, concatenate both.
Basically, what I want
to do is replace the string #1#, #2#, #3#... with the values in $1, $2, $3...
but when trying to do that, perl complains about:

File "test.pl"; Line 15: Can't use string ("1") as a SCALAR ref while
"strict refs" in use

Line 15 being:

$array{$_}=~s/#(\d+)#/$$1/;

I've shown a way to do that in my earlier reply. Your changed
requirements have nothing to do with how to achieve this, you'd have
to do it in any case. So, apply my solution (or another one) to the
new situation. Don't go back to square one, just because an irrelevant
detail has changed.

Anno
 
G

Gunnar Hjalmarsson

Sandman said:
#!/usr/bin/perl
use strict;
use warnings;

my $string = "Hello World!";

my %array = (

Funny name of a hash. ;-)
"Hello (.*?)!", "Goodbye, little #1#!"
# search reply
);
foreach (keys %array){
if ($string=~m/$_/i){
# It matched!

$array{$_}=~s/#(\d+)#/$$1/;
# replace #1# with $1, #2# with $2
# from the match above

Besides that you are trying to use a symbolic (or soft) reference,
which is normally not advisable, you are assuming that the $1 variable
contains what was captured from both the last and the previous
matches, and that appears to be somewhat optimistic...

You need to store what was captured from the first match somewhere,
i.e. in a hash:

my %captures = (
1 => $1,
);
$array{$_} =~ s/#(\d+)#/$captures{$1}/;
 
S

Sandman

Hello again, and thanks to Anno Siegel for your answer to my earlier thread
(on the subject, but
unfortunately, the conditions for my need has slightly altered, and I want
some
further help (from anyone, not just Anno).


Here is a code snippet that probably illustrates what I want to do:

-------------
#!/usr/bin/perl
use strict;
use warnings;

my $string = "Hello World!";

my %array = (
"Hello (.*?)!", "Goodbye, little #1#!"
# search reply
);
foreach (keys %array){
if ($string=~m/$_/i){
# It matched!

$array{$_}=~s/#(\d+)#/$$1/;

Well, this doesn't do what you hope it does. You knew that, and I
showed you a way to do the replacements. Why are you trying it again?[/QUOTE]

I'm not, this is just an illustrative script to givbe the gist of what I want
to do, and while your script worked for the purpose of my earlier needs, it
doesn't for these altered needs, so I went back to this script which doesn't
work, but should give the reader an idea of what I want to achieve.

Plus, your script replaced strings, which I no longer wish to do.
What does that mean? Append it?

Bad wording. The script should output the original string, with the "reply"
printed after it, with #1#, #2#... expanded into the matched values from the
search string.
Trivial. Copy the original, modify the copy, concatenate both.


I've shown a way to do that in my earlier reply. Your changed
requirements have nothing to do with how to achieve this, you'd have
to do it in any case. So, apply my solution (or another one) to the
new situation. Don't go back to square one, just because an irrelevant
detail has changed.

Well, my problem with your earlier script was this (note the altered string):


#!/usr/bin/perl
use strict;
use warnings;


my %tab = (
'Hello (.*?)!' => 'Goodbye, little #1#...',
'Welcome (.*?) and (.*?)!' => 'Farewell, honorable #2# and fair #1#!',
);



for ( "Welcome ladies and gentlemen! Please step inside", "Hello World!" ) {
my $string = $_; # make a copy we can change

while ( my ( $search, $replace) = each %tab ) {
# build ready-to-use replacement string from template
my $n = 1; # submatch number
for ( $string =~ /$search/ ) { # loop over submatches
$replace =~ s/#$n#/$_/g, # substitute one submatch everywhere
$n ++; # next submatch
}
last if $string =~ s/$search/$replace/;
}
print "$string\n";

}

This outputs

Farewell, honorable gentlemen and fair ladies! Please step inside
Goodbye, little World...

Which is undesirable. And, oddly enough, it only worked occasionally with one
specific string (i.e. running the script five times in a row made it match it
two times, highly bizarre).

So, despite your efforts, I felt I wanted to go back to square one and attack
the faulty line in my little script.

Isn't there a way, while using strict refs, to replace #1#, #2#, #3#... with
the values of $1, $2, $3, which are created by a match operation?
 
S

Sandman

foreach (keys %array){
if ($string=~m/$_/i){
# It matched!

$array{$_}=~s/#(\d+)#/$$1/;
# replace #1# with $1, #2# with $2
# from the match above

Besides that you are trying to use a symbolic (or soft) reference,
which is normally not advisable, you are assuming that the $1 variable
contains what was captured from both the last and the previous
matches, and that appears to be somewhat optimistic...

You need to store what was captured from the first match somewhere,
i.e. in a hash:

my %captures = (
1 => $1,
);
$array{$_} =~ s/#(\d+)#/$captures{$1}/;[/QUOTE]

Aaah, this did the trick. Thanks!
 
A

Anno Siegel

[big snip]
Isn't there a way, while using strict refs, to replace #1#, #2#, #3#... with
the values of $1, $2, $3, which are created by a match operation?

Okay, let's treat this in isolation. Suppose you have:

my $string = 'Hello ladies and gentlemen, please step inside';
my $pattern = 'Hello (.*?) and (.*?),';
my $template = 'Farewell honorable #2# and fair #1#';

To do the replacements in the most straightforward way, you could
try this (broken code ahead):

if ( $string =~ /$pattern/ ) {
$template =~ s/#1#/$1/g;
$template =~ s/#2#/$2/g;
}

This doesn't work because we are trying to use $1 and $2 in a
replacement operation. The pattern match that is the first part of
the replacement will destroy the contents of $1 and $2, so we can't
use them that way.

Another problem is that we don't know, in general, how many of $1, $2,...
will be set by $pattern, so we don't know how many replacement lines to
write.

To get around both problems, we can use the fact that a match in list
context returns the captured submatches. So we can loop over them
without knowing how many there are. This action also de-couples the
list elements from the actual variables $1, $2, ..., which is slightly
magical.

We must now generate the marker strings "#1#", "#2#" dynamically.
So:

my $n = 1;
for ( $string =~ /$pattern/ ) {
$template =~ s/#$n#/$_/g;
$n ++;
}

Now $template has been transformed into the reply. $string is
unchanged. This is (in isolation) the same solution I offered before.
Fit it into your scheme of things.

Anno
 
S

Sandman

[big snip]
Isn't there a way, while using strict refs, to replace #1#, #2#, #3#...
with
the values of $1, $2, $3, which are created by a match operation?

Okay, let's treat this in isolation. Suppose you have:

my $string = 'Hello ladies and gentlemen, please step inside';
my $pattern = 'Hello (.*?) and (.*?),';
my $template = 'Farewell honorable #2# and fair #1#';

To do the replacements in the most straightforward way, you could
try this (broken code ahead):

if ( $string =~ /$pattern/ ) {
$template =~ s/#1#/$1/g;
$template =~ s/#2#/$2/g;
}

This doesn't work because we are trying to use $1 and $2 in a
replacement operation. The pattern match that is the first part of
the replacement will destroy the contents of $1 and $2, so we can't
use them that way.

Another problem is that we don't know, in general, how many of $1, $2,...
will be set by $pattern, so we don't know how many replacement lines to
write.

To get around both problems, we can use the fact that a match in list
context returns the captured submatches. So we can loop over them
without knowing how many there are. This action also de-couples the
list elements from the actual variables $1, $2, ..., which is slightly
magical.

We must now generate the marker strings "#1#", "#2#" dynamically.
So:

my $n = 1;
for ( $string =~ /$pattern/ ) {
$template =~ s/#$n#/$_/g;
$n ++;
}

Now $template has been transformed into the reply. $string is
unchanged. This is (in isolation) the same solution I offered before.
Fit it into your scheme of things.

Thanks - I think I now understand what you are doing there in greater detail,
which I didn't before.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,744
Messages
2,569,483
Members
44,903
Latest member
orderPeak8CBDGummies

Latest Threads

Top