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



This might be getting annoying for some of you that I have trouble coming up with my own ideas for such simple things, but the truth is that I am not very experienced with Perl. This is my forth day of programming it. For some reason this script isn't working on my computer. It is supposed go to every entry in the txt file and return the ones with any matching words that you enter. Could you tell me why? In the process, could you possibly give me a couple of pointers on how to make the script more Perl-like?

#/usr/bin/perl print "Search for what: "; chomp(my $search = <STDIN>); @words = split(/ /, $search); open(DATA, "data.txt") or die "error opening file $!"; @data = <DATA>; foreach $data(@data) { foreach $word(@words) { chomp($word); if ($data =~ /$word/) { print $data; } } }

Thanx in advance.

-- zdog (Zenon Zabinski)

Replies are listed 'Best First'.
Re: Trying to make a search Part 2
by swiftone (Curate) on Jun 05, 2000 at 19:47 UTC
    For future reference, please be more specific on what isn't working.

    Your first line:
    #/usr/bin/perl
    Should be:
    #!/usr/bin/perl
    This is an operating system thing, not a perl thing, and won't affect you if you are using a Windows system or calling the script from the command line (e.g. "perl myscript.pl")

    open(DATA, "data.txt") or die "error opening file $!"; @data = <DATA>;
    Update: As many (sean, infoninja, and mdillon have pointed out, @data=<DATA> is correct.
    <strikeout>I believe your problem might be here. <DATA> returns a string. @data is an array. Also, you are only getting the first line from DATA, not all the lines.</strikeout>
    if ($data =~ /$word/) {
    Note that this is case-sensitive. You might want to use /$word/i to make it insenstive.
      forget this, i was talking crap.

      Nuance

      Baldrick, you wouldn't see a subtle plan if it painted itself purple and danced naked on top of a harpsichord, singing "Subtle plans are here again!"

Re: Trying to make a search Part 2
by lhoward (Vicar) on Jun 05, 2000 at 19:55 UTC
    One thing you might find handy is
    use warnings;
    It points out potential problems with your code. (This code doesn't have any, but it is a good technique to use anyway).

    In this case I think your problem is that you're double-chomping $word. When reading input you only need to chomp once to remove the line terminator (CR/LF).

    print "Search for what: "; chomp(my $search = <STDIN>); @words = split(/ /, $search); open(DATA, "data.txt") or die "error opening file $!"; @data = <DATA>; foreach $data(@data) { foreach $word(@words) { if ($data =~ /$word/) { print $data; } } }
      Double chomping doesn't hurt anything. Yeah, its a waste of cycles, but chomp only removes white space characters. Double choping on the other hand would be bad (unless that was what you wanted to do)
Re: Trying to make a search Part 2
by mdillon (Priest) on Jun 05, 2000 at 20:08 UTC
    everybody is wrong!

    @data = <DATA>; does not read only one line. it reads all available lines from the DATA filehandle. has everybody gone mad?

    in scalar context, the <FH> operator returns a single line, but in list context, it returns them all. '@data =' is clearly a list context.

Re: Trying to make a search Part 2
by infoninja (Friar) on Jun 05, 2000 at 20:06 UTC
    Actually, the use of @data will read each remaining line of the filehandle into an array element of @data. The code below illustrates this (assuming that the file test.pl exists in the directory where the code is run):
    #!/usr/bin/perl -w use strict; open(TEST,'test.pl'); my @test = <TEST>; my $line_number = 0; foreach (@test) { ++$line_number; print "$line_number: $_"; }
(zdog) Re: Trying to make a search Part 2
by zdog (Priest) on Jun 05, 2000 at 20:23 UTC

    Cool. Thanx everyone. I finally got it like this:

    #!/usr/bin/perl print "Search for what: "; chomp(my $search = <STDIN>); @words = split(/ /, $search); open(DATA, "books.txt") or die "error opening file $!"; while (<DATA>) { $matches=0; foreach $word(@words) { chomp($word); if (/$word/i) { print unless ($matches > 0); $matches++; } } }

    -- zdog (Zenon Zabinski)
       Go Bells!!

      One thing to note: The regex in your foreach loop has to be compiled several times (potentially) for every line, which can be a performance drain. A better approach is to precompile your regexes using the qr// operator (described in perlop).
      #!/usr/bin/perl print "Search for what: "; chomp(my $search = <STDIN>); @words = split(/ /, $search); # Turn words into regular expressions @words = map {qr/$_/i} @words; # If you don't want the user to have to # worry about regex syntax, use this instead: # @words = map {qr/\Q$_/i} @words open(DATA, "books.txt") or die "error opening file $!"; while (<DATA>) { $matches=0; foreach $word(@words) { if ($_ =~ $word) { print unless ($matches > 0); $matches++; } } }
(zdog) Re: Trying to make a search Part 2
by zdog (Priest) on Jun 05, 2000 at 20:00 UTC

    Thanx guys. I got it to work like this:

    #!/usr/bin/perl use warnings; print "Search for what: "; chomp(my $search = <STDIN>); @words = split(/ /, $search); open(DATA, "books.txt") or die "error opening file $!"; while (<DATA>) { foreach $word(@words) { chomp($word); if (/$word/i) { print; } } }

    But now how would I get it to only print out the entry once even when there are multiple matches

    -- zdog (Zenon Zabinski)
       Go Bells!!

      But now how would I get it to only print out the entry once even when there are multiple matches
      Like so:
      #!/usr/bin/perl use warnings; print "Search for what: "; chomp(my $search = <STDIN>); my %found; #used as a set later @words = split(/ /, $search); open(DATA, "books.txt") or die "error opening file $!"; while (<DATA>) { foreach $word(@words) { chomp($word); if (/$word/i) { print unless defined($found{lc($word)}); $found{lc($word)}=1; #indicates that word has been found } } }
      Update: This prints the line for each word only once. If you instead want to print each line only once, regardless of how many words it matches, try this:
      #!/usr/bin/perl use warnings; print "Search for what: "; chomp(my $search = <STDIN>); @words = split(/ /, $search); open(DATA, "books.txt") or die "error opening file $!"; while (<DATA>) { foreach $word(@words) { chomp($word); if (/$word/i) { print; last; #skips checking this line against oth +er words } } }
      I would use a hash, like this:
      if (/$word/i) { print unless (++$matches{$word} > 1) }
      I suppose you could use $_ in place of $word if you wanted.

      Update: After seeing swiftone's post I realize that I forgot to adjust case. Thanks swiftone.

      if (/$word/i) { print unless (++$matches{lc $word} > 1) }
Re: Trying to make a search Part 2
by Adam (Vicar) on Jun 05, 2000 at 19:44 UTC
    Two things. Don't use the pattern match if eq will work. And @data = <DATA>; only reads the first line of your file.

    UPDATE: I stand corrected.
    As always, I continue to learn daily. Thanks.

      Actually,  @data = <DATA>; will normally slurp in the whole file.
      A filehandle read called in a list context populates the whole file into the list, one line per element.