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

Monks,

I'm writing a script to allow local users to quickly update tables on one of our databases. I'm storing the data in an array, and need to be able to validate each item in that array against a single regexp that's pulled from a config table on the DB.

What I'd ideally *like* to do is something along the lines of

my @array = qw/A B C D E/; @array =~ /ABCDE/gc or warn "Validation failure";
but this doesn't match. I've had a good look through perlre and perlop this morning, but they offered little help for this scenario. A search here turned up Re: regexp searches over array slices, which suggests that stringifying the array would solve the problem, but this doesn't seem to work for the example above (substituting "@array" for @array).

Does anyone know of a way to achieve this? Any suggestions or advice would be very much appreciated.

Thanks in advance ..
-- Foxcub
A friend is someone who can see straight through you, yet still enjoy the view. (Anon)

Replies are listed 'Best First'.
Re: Pattern matching across an array
by broquaint (Abbot) on Feb 05, 2003 at 11:03 UTC
    Assuming you want every element of your array to match then you can simpy use a grep e.g
    my @array1 = qw( foo bar baz ); my @array2 = qw( foo b4r baz ); for my $a (\@array1, \@array2) { print "invalid: @$a\n" if grep !/^[a-z]+\z/, @$a; } __output__ invalid: foo b4r baz

    HTH

    _________
    broquaint

Re: Pattern matching across an array
by tommyw (Hermit) on Feb 05, 2003 at 11:11 UTC

    Your confusion is due to $", the list separator, which defaults to a space. Thus, "@array" has the value A B C D E. You simply need to make allowance for this

    Beware: if you set the separator to '', or use join as robartes has suggested, then you won't differentiate between qw/AB CD E/ and qw/A B C D E/, which may or may not be what you want (similarly, if you manage to get a space into one of the array values, this may cause problems.)

    --
    Tommy
    Too stupid to live.
    Too stubborn to die.

      But @array isn't interpolated anywhere. The =~ operator doesn't impose any magical interpolation of the LHS. Instead, @array is in scalar context, and @array in scalar context is the length of the array.

      my @foo = 1..5; print @foo =~ /5/; # prints '1' for success.
      Update: Oops, sorry, my bad. Missed the parenthesis in the OP.

      ihb

        If you notice, Foxcub has tried stringifying the array, by enclosing it in quotes. That's where the interpolation happens.

        my @foo = 1..5; print "@foo" =~ /1 2 3 4 5/; # prints '1' for success.

        --
        Tommy
        Too stupid to live.
        Too stubborn to die.

Re: Pattern matching across an array
by robartes (Priest) on Feb 05, 2003 at 11:04 UTC
    If I understand your question correctly, join is the way to go:
    my @array = qw/A B C D E/; (join '',@array)=~/ABCDE/gc or warn "Validation failure";

    CU
    Robartes-

Re: Pattern matching across an array
by jdporter (Paladin) on Feb 05, 2003 at 11:42 UTC
    It's hard to tell exactly what your validation criteria really are.
    The way I hear it is, all the names in the array must be matched by the regex, or else there's a validation failure.

    If all the names are single letters, as in your example, then the regex could look like this: /^[ABCDE]$/. That's known as a "character class".
    However, if the names might be longer, e.g. qw/foo bar quux/, then the regex could use "alternation", like so: /^(foo|bar|quux)$/.

    Then, to apply the regex (whichever way it's formulated), you could do this:
    my @failures = grep { !/$regex/ } @array; if ( @failures ) { warn "Validation failure. Bad names: @failures\n"; }

    Now, if what you're really wanting to do is ascertain that the given names are all in an "approved" set of names, a regex may not be the clearest way of implementing this. I'd recommend using a hash as a set, and using the exists function to determine set membership.
    my %valid_names; @valid_names{ qw/ A B C D E / } = (); # define the set of valid name +s. my @failures = grep { ! exists $valid_names{$_} } @array; # and so forth.
    Of course, this only works neatly when the names are all exact. If there is any "fuzziness", such as what you might use wildcards for, then regexes are probably the way to go.

    jdporter
    The 6th Rule of Perl Club is -- There is no Rule #6.

      Ah, no, sorry. I should have said that. A - E represent DB table fields, which are validated by the respective component of the regexp.

      I can't use character classes or alternation simply because each attribute has separate validation criteria. I'm trying to concatenate those critera into one regular expression which I can then apply to the whole array, rather than having to store a set of separate regexps for each attribute on the DB.

      Changing $" to '' seems to have done the trick .. thanks to everyone for the help.

      -- Foxcub
      A friend is someone who can see straight through you, yet still enjoy the view. (Anon)

        Ah, I see.
        Well, I question the wisdom of your approach... but if that's the way you want to do it, then you should at least make sure that the character you're using to separate the values doesn't occur in any of the values.

        jdporter
        The 6th Rule of Perl Club is -- There is no Rule #6.

Re: Pattern matching across an array
by hardburn (Abbot) on Feb 05, 2003 at 15:11 UTC
    my @array = qw(A B C D E); join('|', @array) =~ /\A(\|?ABCDE\|?)+\z/g or warn "Validation failure +\n";

    Untested, as always. You'll have to choose the seperator char carefuly, of course. Note that the quantifier ? after each \| is important to handle the case of the start and end of the string.

    Update: Added /g modifier.

    ----
    Invent a rounder wheel.