perl-diddler has asked for the wisdom of the Perl Monks concerning the following question:

I have a table of, "mostly" flags that select actions arranged in a hash, where the flags are the keys. Of course problems expand to overflow original designs and I'm wondering how to expand in a way that doesn't invalidate everything (as other programs use the format and I just want to be able to match a regex in my key and then take the action.

First thing to note is that the keys are literals and that storing a "qr{([0-6BS])}" can only be safely stored as a string, since compiled, it becomes a Regexp-ref, and ref's don't store too well in strings. So I'm trying to figure out a way to convert that string to a usable RE and have it return a captured value of the single-character matched.

A form that works for matching (but not for putting in the hash table because it converts the key to a ref) is:

> perl -we'use strict; use P; my $re_str=qr{([0-6BS])}; $_="B"; my $ans; if (eval m{$re_str}) { $ans=$1; } P "ans=%s, re_s=%s", $ans, $re_str; ' ans=B, re_s=(?^:([0-6BS]))
I included that example so you could see what type of output I wanted (i.e. "ans containing 'B').

As soon as I turn my re_str into a string though I start having problems.

my $re_str="qr{([0-6BS])}"; $_="B"; my $ans; if (eval m{$re_str}) { $ans=$1; } P "ans=%s, re_s=%s", $ans, $re_str;'
ans=∄, re_s=qr{([0-6BS])}
In this case, it looks like the if wasn't taken so nothing got assigned to ans. In any case, $ans contains an undef (the '∄' symbol literally means "there does not exist").

I've tried several variations in the eval, including assigning the string re to another var and using that in my match, like:

my $re_str="qr{([0-6BS])}"; $_="B"; my $ans; if (eval P (q(my $re=%s; m{$re}), $re_str) ) { $ans=$1; } P "ans=%s, re_s=%s", $ans, $re_str;'
ans=∄, re_s=qr{([0-6BS])}
But no change. I've tried storing the result to a package var by changing the "my $ans" to "our $ans". no good.

I feel like there some be some obvious, simple solution for something so trivial, but it is eluding me.

Could someone point out what I might use instead?

Ideally, instead of using "$ans" I could use something like

my $re_str="qr{([0-6BS])}"; $_="B"; our @ans = m{$re_str}; P "ans=%s, re_s=%s", \@ans, $re_str;'
ans=[], re_s=qr{([0-6BS])}

to allow the possibility of multiple matches in "re_str" being captured, but an empty array is definitely not so helpful and any value returned would be better than none.

Ideas?..... Thanks!

Replies are listed 'Best First'.
Re: how to use string RE (against $_ and capture)
by dave_the_m (Monsignor) on Nov 07, 2016 at 23:16 UTC
    I'm not entirely sure I understand what you're trying to achieve, but dereffing and stringifying a qr// ref gives you a usable pattern string. For example:
    my $re1 = qr{([0-4])}; my $re2 = qr{([5-9])}; my %hash = ("$$re1" => "foo", "$$re2" => "bar"); $_ = "-5-"; for my $re (keys %hash) { print "match: ($1) $hash{$re}\n" if /$re/; } # outputs: # match: (5) bar
    Of course in the above it's now re-compiling the regex when executing it.

    Dave.

      My problem has come in being able to recognize when I have a regex vs. a regular string in a key. I tried using a "ref $re", looking for a Regexp (no go), and I tried looking for "qr" -- only works if you store qr as a string initially, and try to evaluate it later. That's what I ended up trying to do. Looking for something like "(?" makes me a bit less comfortable than searching for "qr", though -- it looks a bit sloppy for some reason, but maybe I'm just not used it.
        I tried using a "ref $re", looking for a Regexp (no go) ...

        I'm not sure what this means. Again, my take:

        c:\@Work\Perl\monks>perl -wMstrict -le "my $re_obj = qr{([0-6BS])}; if (ref $re_obj) { print 'A: reference: ', ref $re_obj; } else { print 'A: not a reference'; } ;; my $re_str = '' . $re_obj; if (ref $re_str) { print 'B: reference: ', ref $re_str; } else { print 'B: not a reference'; } " A: reference: Regexp B: not a reference


        Give a man a fish:  <%-{-{-{-<

Re: how to use string RE (against $_ and capture)
by kcott (Archbishop) on Nov 08, 2016 at 01:45 UTC

    G'day perl-diddler,

    Firstly, I suggest you read "perlop: Regexp Quote-Like Operators". There are certain things about qr that are non-standard and non-intuitive: I suspect you're making understandable, yet incorrect, assumptions.

    In the following, I've addressed all (I think) of the points you've raised and hopefully cleared up any misunderstandings.

    When used as a string, qr acts like a string:

    $ perl -E 'my $re = qr{([0-6BS])}; say $re' (?^u:([0-6BS]))

    When used as a reference, qr acts like a reference:

    $ perl -E 'my $re = qr{([0-6BS])}; say ref($re)' Regexp

    When used as a hash key, which is a string, qr acts like a string:

    $ perl -E 'my $re = qr{([0-6BS])}; my %x = ($re, 1); my @y = keys %x; +say $y[0]' (?^u:([0-6BS]))

    The hash key is just a string, not a reference:

    $ perl -E 'my $re = qr{([0-6BS])}; my %x = ($re, 1); my @y = keys %x; +say "|", ref($y[0]), "|"' ||

    The next two points directly address the title of your post: "how to use string RE (against $_ and capture)".

    You can use qr in a match like '/COMPILED_QR_RE/':

    $ perl -E 'my $re = qr{([0-6BS])}; $_ = "B"; /$re/; say $1' B

    You can use a hash key string in a match like '/STRING_RE/':

    $ perl -E 'my $re = qr{([0-6BS])}; my %x = ($re, 1); my @y = keys %x; +$_ = "B"; /$y[0]/; say $1' B

    See also: perlre and ref.

    — Ken

Re: how to use string RE (against $_ and capture)
by AnomalousMonk (Archbishop) on Nov 07, 2016 at 23:44 UTC

    Here's my take:

    c:\@Work\Perl\monks>perl -wMstrict -le "my $re_obj = qr{([0-6BS])}; my $re_str = '' . $re_obj; print $re_str; ;; if ('ABC' =~ $re_str) { print qq{match, \$1 captured '$1'}; } else { print 'no match'; } " (?^:([0-6BS])) match, $1 captured 'B'


    Give a man a fish:  <%-{-{-{-<

      What I meant is how, after "$re_str" has been stringified, would the code know the string is meant to be used as a regexp instead of a direct compare. The keys represent command-line flags or switches after one dash has been removed, but in this case I wanted the key to match several possibilities.

      As I noted in the other response, it looks like searching for '(?' at the beginning of the expression is the best way.

        ... how, after "$re_str" has been stringified, would the code know the string is meant to be used as a regexp instead of a direct compare.

        The code "knows" because the string is used by a   =~  m//  s/// (I think that's all of them) matching operator. And what's a "direct compare" anyway?


        Give a man a fish:  <%-{-{-{-<

Re: how to use string RE (against $_ and capture)
by AnomalousMonk (Archbishop) on Nov 08, 2016 at 00:41 UTC
    ... multiple matches in "re_str" being captured ...

    c:\@Work\Perl\monks>perl -wMstrict -le "use Data::Dump qw(pp); ;; my $re_str1 = '' . qr{ [0-6BS] }xms; print 'A: reference: ', ref $re_str1 if ref $re_str1; ;; my $str = 'ab3c64dSeBf'; my @captures = $str =~ m{ $re_str1 }xmsg; pp @captures; ;; my $re_str2 = '' . qr{ [a-f] }xms; print 'B: reference: ', ref $re_str2 if ref $re_str2; ;; my ($one, $two, $three) = $str =~ m{ ($re_str1) ($re_str2) ($re_str1) + }xms; pp $one, $two, $three; " (3, 6, 4, "S", "B") (3, "c", 6)
    Note the  /g modifier in the
        my @captures = $str =~ m{ $re_str1 }xmsg;
    statement.


    Give a man a fish:  <%-{-{-{-<

Re: how to use string RE (against $_ and capture)
by Anonymous Monk on Nov 08, 2016 at 03:18 UTC

    As soon as I turn my re_str into a string though I start having problems. eval m{$re_str}

    Have you read eval? What string does a m{$re_str} return , are you sure you want to eval that user supplied input?

    See (user supplied regex substitution without eval)

Re: how to use string RE (against $_ and capture)
by RonW (Parson) on Nov 11, 2016 at 21:44 UTC

    In your first code sample, you have:

    my $re_str=qr{(&#x005b;0-6BS&#x005d;)}; $_="B"; my $ans; if (eval m{$re_str})

    Why are you using eval? It should not be needed.

    In your second code sample:

    my $re_str="qr{(&#91;0-6BS&#93;)}"; $_="B"; my $ans; if (eval m{$re_str})

    The m{$re_str} will be interpreted as m/qr{(&#x005b;0-6BS&#x005d;)}/ which is looking to match something that starts with "qr". (And I'm not sure how the "{([0-6BS])}" will get interpreted as. "{" and "}" in a regex are normally used to quantify the number of matches.)

    To store a regex as a hash key, you could prefix the regex with a character that won't otherwise appear as the first character of any of your hash keys. So, something like this:

    #not tested, YMMV for $key (keys %options) { if ($key =~ s/^#//) { if (/$key/) { print "Match: $1\n"; } } else } if ($_ eq $key) { print "Equals: $1\n"; } } }
      ... I'm not sure how the "{([0-6BS])}" will get interpreted as. "{" and "}" in a regex are normally used to quantify the number of matches.

      Apparently, if the RE compiler can't see anything in the regex that looks like a count or count range, it just gives up on the whole counted quantifier thing and takes the curlies as literal characters.

      c:\@Work\Perl>perl -wMstrict -le "$_ = 'xxqr{S}xx'; print qq{matched '$&', \$1 is '$1'} if m/qr{([0-6BS])}/; " matched 'qr{S}', $1 is 'S'
      (Tested under ActiveState 5.8.9, Strawberry 5.14.4.1.)

      To store a regex as a hash key, you could prefix the regex with a character that won't otherwise appear as the first character of any of your has keys.

      I still prefer some sort of separate tag to distinguish pattern from exact matching:

      c:\@Work\Perl>perl -wMstrict -MData::Dump -le "my %options = ( qr{ x (Y) z }xms => { type => 'pattern', name => 'foo', }, '(?x) x (Y) z ' => { type => 'pattern', name => 'fum', }, 'xYzzy' => { type => 'exact', name => 'bar', }, 'zzzzz' => { type => '?????', name => 'zot', }, ); dd \%options; ;; $_ = 'xYzzy'; for my $p (sort keys %options) { my ($p_type, $p_name) = @{ $options{$p} }{ qw(type name) }; if ($p_type eq 'pattern') { if ($_ =~ $p) { print qq{'$p_name' pattern match of $p, \$1 is '$ +1'}; } } elsif ($p_type eq 'exact') { if ($_ eq $p) { print qq{'$p_name' exact match of '$p'}; } } else { die qq{unknown: $p; type '$p_type'; name '$p_name'}; } } " { "(?^msx: x (Y) z )" => { name => "foo", type => "pattern" }, "(?x) x (Y) z " => { name => "fum", type => "pattern" }, "xYzzy" => { name => "bar", type => "exact" }, "zzzzz" => { name => "zot", type => "?????" }, } 'foo' pattern match of (?^msx: x (Y) z ), $1 is 'Y' 'fum' pattern match of (?x) x (Y) z , $1 is 'Y' 'bar' exact match of 'xYzzy' unknown: zzzzz; type '?????'; name 'zot' at -e line 1.
      (Test under same Perl versions as above.)


      Give a man a fish:  <%-{-{-{-<