Beefy Boxes and Bandwidth Generously Provided by pair Networks
We don't bite newbies here... much

How can grep search based on text or pattern based on user input?

by Russ (Deacon)
on Dec 02, 2000 at 08:11 UTC ( #44553=perlquestion: print w/replies, xml ) Need Help??
Russ has asked for the wisdom of the Perl Monks concerning the following question:

I've got a CGI that accepts a single form field (name=searchtext) which I put into $searchtext. I'd like to offer the un-Perl-knowledgeable user to simply enter a word with * wildcards like any normal search, but at the same time offer the Perl-knowledgeable user the ability to enter a pattern.

I'm assuming that the un-Perl will enter something like "surf*g" and the Perl user will enter something like "/surf\w*/i" or similar. (I'm assuming that all searches by the un-Perl are case insensitive.)

Here's what I have so far:

if ($searchtext =~ /^\/.*\/\w+$/) { $searchtext =~ s#^\s+/|/\s+$|/\w+\s+$#/#g; } else { $searchtext =~ s/([\\\+\$\^])/\\$1/g; $searchtext =~ s/\*/\\w\*/g; $searchtext = '/' . $searchtext . '/i'; } foreach (sort {$a <=> $b} grep $searchtext, grep !/^#/, <STUFF>) {

Problem is, this doesn't work. And even if it did, I'm sure it's nowhere close to being the most efficient code around. Any suggestions? Thanks. :)

Replies are listed 'Best First'.
Re: How can grep search based on text or pattern based on user input?
by chromatic (Archbishop) on Dec 02, 2000 at 08:21 UTC
    A regex (or the left hand side of a substitution) performs interpolation on any variables inside, a la:

    foreach (sort {$a <=> $b} grep /$searchtext/, grep !/^#/, <STUFF>) {

    That means that you'll have to strip off the slashes on a user-supplied regex, and not add them in on the other option.

    As for efficiency, it's not too bad. I'd get rid of the double grep though, by slurping everything in at once (if it's a small file), skipping commented lines, or rewriting the loop condition somewhat.

      First off, thanks all for your previous help.

      OK, while I know a radio button would be easiest for this solution, I'm fairly confident that most of my users will just get confused. But I still want to offer the extra ability to those wise few. (Besides, this is a fun little learning project.) So I need to just do everything auto-magicly. If I can't remove the "/"s from the grep line, can I do something like:

      grep /$searchtext/$modifiers @stuff

      Problem is, I'm reasonably sure this will not work. I also am nervous about this not being able to handle any regex the highly-skilled user may input. Essentially, I'm looking at getting the following to work:

      @stuff = ('This is some text.','This is more text.'); $stext = '/some/i'; ($null, $text, $mod) = split(/\//, $stext); foreach (grep /$text/$mod, @stuff) { print $_, "\n"; }

      Can anyone get this to work? BTW, please tell me if I'm being an annoying-newbie, and I'll go away.

        Removing the slashes is pretty easy. Here's one (non-optimal) attempt:

        $input =~ s!^/(.+)/$!$1!;

        With that in place, everything you've described so far, except for the $modifiers, will work. You'd either have to escape everything and wrap the whole grep in an eval block, or use some sort of checkbox or if-else block to check for a case insensitive flag.

        Perl won't (as far as I know, and I'm pretty sure about this having just tested it) interpolate a variable in the regex flag position.

Re: How can grep search based on text or pattern based on user input?
by dchetlin (Friar) on Dec 04, 2000 at 11:44 UTC
    `grep' in Perl is a misnomer; it's not inherently related to RExen. It should really be named `filter' or something similar. As such, using a bare variable as the `EXPR' in grep EXPR, LIST won't invoke the REx engine at all -- basically you're checking $searchtext for truth each time through the `LIST', which is most certainly not WYM.

    My personal vote on this problem would be to provide the users a radio button group allowing them to choose whether they want to use Perl-based or standard search-engine searches, rather than trying to come up with a heuristic to determine if $searchtext is a Perl REx or just a term.

    However, if you really want to do that, you'll want to look into `\Q' and `\E' to quote characters in a REx, rather than going to all that trouble (on line 4) to take care of metacharacters. I'm skeptical of the conversion from `*' to `\w*', but I suppose it's a sensible choice.

    Rather than trying to build up a string that syntactically resembles a REx in Perl code, build the actual search pattern and then use it in the grep inside of a REx:

    grep m/$searchtext/, <STUFF>

    You might also want to look into qr// for a more efficient and cleaner way to build RExen programatically.


Re: How can grep search based on text or pattern based on user input?
by Anonymous Monk on Dec 02, 2000 at 05:35 UTC
    I would suggest not allowing the users to chose wether or not their search is case insensitive.
    Then you could strip out the leading and trailing /'s if somebody were to enter your Perl-knowledgeable regex. Change all
    lonely *'s to .+?'s or some such. Eval it to make sure it's valid. then do your search.

    From the Ram Book
    sub is_valid_pattern { my $pat = shift; return eval { "" =~ /$pat/; 1 } || 0; }
Re: How can grep search based on text or pattern based on user input?
by Anonymous Monk on Dec 02, 2000 at 03:39 UTC
    What I would do is have a dropdown or radio list saying which method they'd like to search with. If one isn't specified, default to a dumb grep-style search.. that'd simplify things some..

Log In?

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://44553]
Approved by root
and all is quiet...

How do I use this? | Other CB clients
Other Users?
Others taking refuge in the Monastery: (11)
As of 2017-05-30 13:16 GMT
Find Nodes?
    Voting Booth?