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

Greetings,
Once again I must turn to the Monks for some help. The code below is meant to search a text file and it does not do it, but I feel that I am close.
I have a text file named fai.txt The information in it is laid out as follows;

blah|blah|blah|blah
this|this|this|this

the fields are all seperated by |
when a part number is entered I want to search the text file and if the first field matches the part number entered then I want that entire string to be printed to the screen.
One of the error messages I get is that "Global symbol @fields requires explicit package name" I though I declared it in the subroutine. I would appreciate any poke in the right direction on this. Thanks, Kerry
print "What is the Part Number that you would like to look up?\n"; my $PartNumber = <STDIN>; chomp($PartNumber); while (!$PartNumber){ ## while $PartNumber is empty keep asking print "PartNumber? "; $PartNumber = <STDIN>; chomp($PartNumber); } &search; #this is the subroutine to search the file sub search{ open(FILE, '>>fai.txt') || die $!; while (<FILE>) { chomp; # remove newline my @fields = split(/\|/, $_); # test whether the search string matches part number if ($fields[0] =~ /$PartNumber/ ) { print "$PartNumber Rev. $Revision has a First Article +Report\nWould you like to view it?"; } }} close(FILE); my $answer = <STDIN>; chomp($answer); if ($answer eq "yes") { print "\n\n\none second please......\n"; print "retrieving file........\n"; <b>error caused by this--></b> print "$fields[0]: $fields[1]: $fields[ +2]: $fields[3]\n"; } # If the answer is no then die else { print "ok, well have a nice day.\n"; }
credo quia absurdum

update (broquaint): s|<(/)?pre>|<${1}code>|g

Replies are listed 'Best First'.
Re: pulling information from a delineated text file using a subroutine
by Ovid (Cardinal) on Jan 16, 2003 at 03:24 UTC

    Amongst other things, the following line is in error and will cause your script to fail:

    open(FILE, '>>fai.txt') || die $!;

    The >> cause the file to be opened in "append" mode. You want "read" mode. You can do this to correct this:

    sub search { my ($file,$part_number,$revision) = @_; open FILE "< $file" or die "Cannot open ($file) for reading: $ +!"; local $_; while (<FILE>) { # search line for $part_number

    Note that I have have made some changes. First, I have explicitly passed the necessary arguments to the subroutine. This makes it easier to reuse or move to a library, if necessary. Further, I've localized $_. This restricts changes to $_ to the current scope, thus ensuring that if another part of your program uses $_, you won't accidentally have a strange value in there. See perldoc -f local for more information on this.

    Cheers,
    Ovid

    New address of my CGI Course.
    Silence is Evil (feel free to copy and distribute widely - note copyright text)

      I see the error for the append option, but your post confused me. If I use your code as you typed it, where does it know to open the fai.txt file? Will the rest of the while block stay the same? Thanks again,
      Kerry credo quia absurdum
Re: pulling information from a delineated text file using a subroutine
by tall_man (Parson) on Jan 16, 2003 at 04:17 UTC
    To answer your main issue:
    I thought I declared it in the subroutine.

    Yes, that is exactly what you did. You declared @fields inside of search, and that is the only place it is known, because "my" creates a scoped variable. It goes out of scope outside the subroutine.

    That wouldn't have been a problem if you only used @fields inside search, which I think you meant to do. But because you accidently doubled the "}" right after the end of the while loop, you ended the subroutine definition right there. You need to move that "}" to the end of the program, and then the use of @fields will still be in scope for the print statement.

    P.S.: Other than the "<" fix, the other suggestions by Ovid are more a matter of good programming style than they are fixes to make your program work correctly. Ovid is suggesting that you pass in the file name as a parameter, like this:

    &search("fai.txt", $PartNumber, $Revision);
Re: pulling information from a delineated text file using a subroutine
by Ryszard (Priest) on Jan 16, 2003 at 07:14 UTC
    Here is a rather verbose solution:
    #!/usr/bin/perl -w use strict; use Data::Dumper; print "Enter the part you which to search for: "; my $part = <STDIN>; chomp($part); my $searchresult = &search(part => $part); if (defined $searchresult) { print "Located part No. $part: $$searchresult[1]\n"; } else { print "Your part ($part) could not be found...\n"; } # This routine will accept a part number as an anonymous # hash, and seach thru' a text file returning the entire # record (pipe delimited) of the 1st occurrence sub search { my %args = @_; my $retval; local*FH; open (FH, './parts.txt') || die "Cannot open file: ($!)"; my @parts = <FH>; foreach my $line (@parts) { my @fields = split(/\|/, $line); if ($args{part} eq $fields[0]) { $retval = \@fields; last; } } close FH; return $retval; }

    There are a number of problems with this kind of design (including, but not limited to:)

    1. Enforcement of primary key integrity (there is none)
    2. Locking of the file during updates
    3. Scalability of the file
    IMHO, this would be considered a one off solution, not really production quality. One step toward production quality would be to stuff it all into a database!

    Putting this kind of information in a database is quite useful as the data can but cut up in any particular way, normalised (part number, description, who ordered the part), the data can be backed up using standard database backup methods, it can be rolledback (if your DB engine supports it) etc etc etc.

Re: pulling information from a delineated text file using a subroutine
by Coruscate (Sexton) on Jan 16, 2003 at 07:51 UTC

    #!/usr/bin/perl -w use strict; sub get_part_num { my $part; while (!$part || $part =~ /[^0-9]/) { print "Part Number? "; chomp( $part = <STDIN> ); } return $part; } sub search { my ($file, $part) = @_; open my $db, "<$file" or die "Could not read file: $!"; while (<$db>) { chomp; my @fields = split(/\|/, $_); print "Part $part found: \n\t", join("\n\t", @fields[1..$#fields]), "\n" if $fields[0] eq $part; } } search( 'fai.txt', get_part_num() );

      What happens if you want part number 0 ?

      -- Hofmator

        I didn't really consider this option as I suppose I would never assign a product to ID 0. Zero isn't really a number, therefore why would I assign a product to a not-really-a-number ID? :P

        Anyway, if you really did need part 0, then just change while (!$part || $part =~ /[^0-9]/) { to while (!defined $part || $part =~ /[^0-9]/) {. The only thing this will also change is if the user hits enter without entering anything, the program will exit. That's fine by my standards

Re: pulling information from a delineated text file using a subroutine
by jdporter (Paladin) on Jan 16, 2003 at 17:56 UTC
    Another solution, hopefully easy to understand.
    sub find_matching_part_entry { my( $filename, $part, $rev ) = @_; open F, "< $filename" or die "Error reading $filename: $!\n"; my @match; while (<F>) { chomp; my @fields = split /\|/; if ( $fields[0] eq $part && $fields[1] eq $rev ) { @match = @fields; } } close F; return @match; } my @matching_entry = find_matching_part_entry( "fai.txt", $part, $rev +); if ( @matching_entry ) { print "Found part:\n"; for ( @matching_entry ) { print "$_\n"; } } else { print "No part matching $part, rev $rev found!\n"; }
    Note - No global variables.

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