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

I'm trying to match a word in the database and record it's occurence, I'm just using a form to post to the script. My form:
<FORM ACTION="cgi-bin/basic2.pl" METHOD="POST"> <SELECT NAME="browser"> <OPTION VALUE="microsoft">microsoft</OPTION> <OPTION VALUE="netscape">netscape</OPTION> <OPTION VALUE="aol">aol</OPTION> <OPTION VALUE="mozilla">mozilla</OPTION> <OPTION VALUE="opera">opera</OPTION> </SELECT> <INPUT TYPE="SUBMIT" VALUE="Submit"> </FORM>
My code:

#!/perl/bin/perl print "Content-type: text/html\n\n"; $logpath = "data/browser.dat"; &parse; sub parse{ if ($ENV{'REQUEST_METHOD'} eq 'GET') { @pairs = split(/&/, $ENV{'QUERY_STRING'}); } elsif ($ENV{'REQUEST_METHOD'} eq 'POST') { read(STDIN, $buffer, $ENV{'CONTENT_LENGTH'}); @pairs = split(/&/, $buffer); } else {exit;} foreach $pair (@pairs) { ($name, $value) = split(/=/, $pair); $value =~ tr/+/ /; $value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg; $value =~ s/<!--(.|\n)*-->//g; $form{$name} = $value; } } $browser = $form{'browser'}; open (LOG, "$logpath"); @data = <LOG>; close(LOG); $not_found = 1; open (LOG, ">$logpath"); foreach $line(@data) { chomp($line); ($fcount,$fbrowser)= split(/\|/,$line); if ($fbrowser eq $browser) { $not_found = 0; $fcount = $fcount+1; print LOG "$fcount|$fbrowser\n"; } else { print "$line"; } } close(LOG); if($not_found) { open (LOG,">>$logpath"); print "1|$browser"; close(LOG); } # just prints out log to browser open (LOG, "$logpath"); @data = <LOG>; close(LOG); foreach $line(@data) { chomp($line); print "$line<br>"; } close(LOG); print "<p>$not_found";
It's is always not_found

Janitored by Arunbear - added readmore tags, as per Monastery guidelines

Replies are listed 'Best First'.
Re: Need help with searching flatfile and updating it.
by Fletch (Bishop) on Apr 25, 2005 at 01:57 UTC
    • use CGI, don't reinvent the wheel.
    • ALWAYS CHECK THE RETURN ON SYSTEM CALLS SUCH AS open()
Re: Need help with searching flatfile and updating it.
by chas (Priest) on Apr 25, 2005 at 02:14 UTC
    It isn't at all clear to me that your splitting on '&' of $buffer or $ENV{'QUERY_STRING'} (and your further decoding) is going to get you the exact data you need. It may...I'm not claiming it definitely won't, but life would be much easier if you use CGI.pm and the "param" method to get the data you need. You won't have to worry about GET or POST methods and the decoding of the encoded data will be done automatically for you. If you are going to use Perl, make use of one of the most useful modules available; you'll be glad you did!
    chas
    A reply falls below the community's threshold of quality. You may see it by logging in.
Re: Need help with searching flatfile and updating it.
by jasonk (Parson) on Apr 25, 2005 at 03:50 UTC

    The bigger problems you have with re-inventing wheels that should not be reinvented notwithstanding, your main problem is the fundamentally broken loop that is looking at the data that came from the file. Without resorting to rewriting code for you, in english your program does this:

    • Open $logpath and read in the contents
    • Iterate through the lines found there, and
      • If the browser in this line is the one from the form, increment it's counter and print it to the log
      • If the browser in this line is not the one from the form, print it to STDOUT

    In a nutshell, this means that the browser that matches the form entry gets printed to the log, while the others get printed to the browser. I bet if you looked at the logfile, you would find it always contains at most 1 line.

    There are however, several other problems with this code:

    • Always, Always, Always use '-w' and 'use strict'.
    • Doing your own parameter parsing instead of using CGI is just asking for trouble.
    • Not checking the return code from your open calls means you may be truncating your logfile to rewrite it, even though the script didn't read any data from it in the first place.
    • Not handling locking means that if two people submit the form at the same time, your logfile could very likely be nuked by the second one.

    We're not surrounded, we're in a target-rich environment!
      "In a nutshell, this means that the browser that matches the form entry gets printed to the log, while the others get printed to the browser."
      I forgot to remark on that in my reply, but I didn't undertand the point of that print either. In fact, it isn't even being printed to the browser I think, because the print "Content-type: text/html\n\n"; doesn't occur till later.
      chas
Thanks for everybody's help!
by Anonymous Monk on Apr 25, 2005 at 05:23 UTC
    Once we muddled through the cgi.pm trench, the main problem was the broken loop or the script printing to the browser instead of the file.

    It didn't cause an error so I didn't notice it.

    Thanks again everybody.

Re: Need help with searching flatfile and updating it.
by pearlie (Sexton) on Apr 25, 2005 at 04:46 UTC
    Hello,
    You can use Tie::file module to read and update the file.
I have it working, but sorting is very greedy.
by Anonymous Monk on Apr 25, 2005 at 05:11 UTC
    #!/perl/bin/perl -w use CGI; $q = new CGI; for $key ( $q->param() ) { $form{$key} = $q->param($key); } $logpath = "data/browser.dat"; $temppath = "C:/Documents and Settings/Owner/My Documents/My Website/c +gi-bin/data/temp.dat"; $browser = $form{'browser'}; open (LOG, "$logpath"); @data = <LOG>; close(LOG); $not_found = 1; if($browser) { open (LOG, ">$logpath"); foreach $line(@data) { chomp($line); ($fcount,$fbrowser)= split(/\|/,$line); if ($fbrowser eq $browser) { $not_found = 0; $fcount = $fcount+1; print LOG "$fcount|$fbrowser\n"; } else { print LOG "$line\n"; } } close(LOG); if($not_found) { open (LOG,">>$logpath"); print LOG "1|$browser"; close(LOG); } } # prints out log and form to browser print "Content-type: text/html\n\n"; open (LOG, "$logpath"); @data = <LOG>; close(LOG); foreach $line(@data) { chomp($line); print "$line<br>"; } close(LOG); print <<EOT; <FORM ACTION="basic.pl" METHOD="POST"> <SELECT NAME="browser"> <OPTION VALUE="microsoft">microsoft</OPTION> <OPTION VALUE="netscape">netscape</OPTION> <OPTION VALUE="aol">aol</OPTION> <OPTION VALUE="mozilla">mozilla</OPTION> <OPTION VALUE="opera">opera</OPTION> </SELECT> <INPUT TYPE="SUBMIT" VALUE="Submit"> </FORM> EOT # If you would like to suggest a better way to sort here is the rest o +f the code. sub getBrowsers { %browse=(); open(INF,"$logpath") || print "Cannot open logs.dat file\n"; @hits= <INF>; close(INF); open(TEMP,">>$temppath") || print "Cannot open temp.dat file\n"; foreach $i (@hits) { chomp($i); ($hits,$browser)= split(/\|/,$i); for ($t=1;$t<$hits;$t++) { print TEMP "$browser\n"; } } close(TEMP); open(TEMP,"$temppath") || print "Cannot open temp.dat file\n"; @temp= <TEMP>; close(TEMP); foreach $i (@temp) { $browse{$i}++; } print <<EOT; <TABLE width="99%" cellpadding="3" cellspacing="1" border="0" bgCo +lor="#0080ff"> <TR><TD bgColor="#ccccff" height="25" colspan="2"><SPAN class="tit +le">Top Browsers</SPAN></TD></TR> EOT $max = 1; foreach $key (sort BrowserSort (keys(%browse))) { ($max > $browse{$key}) || ($max = $browse{$key}); ($max >= 401) && ($divisor = $max/401); ($max < 401) && ($multiplier = 401/$max); ($divisor) && ($width = sprintf("%0d",$browse{$key}/$divisor)); ($multiplier) && ($width = sprintf("%0d",$browse{$key} * $multipli +er)); print "<tr><td bgColor=\#FFFFFF align=left><a href=$key>$key</a><b +r><img src=../images/pix.gif width=1 height=2><br> <img src=../images/blue.gif width=$width height=10></td><td bg +Color=\#FFFFFF align=right valign=bottom>$browse{$key}</td></tr>\n"; } print "</table><p>\n"; } sub BrowserSort { $browse{$b} <=> $browse{$a}; } &getBrowsers; unlink($temppath);
      sorting is very greedy

      Have you read perlre and the Regex Tutorials? Or perhaps you were referring to something else entirely. If you were to post the specific excerpt or at the least be more specific with the regard to the problem, perhaps we can help.

        What I did was take the information in the database and split the lines, ran them through a for loop and printed them to a temp file. So if the first line was 12|netscape, then I printed netscape 12 times in the temp file, if the second line was 10|microsoft, I printed it 10 times in the temp file, and recounted them and resorted them. This is never going to work when I get to number above 1000. What I would like to do is just sort the lines in the original database numerically, on the first field, but with out rewritting them, if that's possible.

        Take the code and chop off everthing after EOT and see if you can sort the $logpath file numerically.

        #!/perl/bin/perl -w use CGI; $q = new CGI; for $key ( $q->param() ) { $form{$key} = $q->param($key); } $logpath = "data/browser.dat"; $browser = $form{'browser'}; open (LOG, "$logpath"); @data = <LOG>; close(LOG); $not_found = 1; if($browser) { open (LOG, ">$logpath"); foreach $line(@data) { chomp($line); ($fcount,$fbrowser)= split(/\|/,$line); if ($fbrowser eq $browser) { $not_found = 0; $fcount = $fcount+1; print LOG "$fcount|$fbrowser\n"; } else { print LOG "$line\n"; } } close(LOG); if($not_found) { open (LOG,">>$logpath"); print LOG "1|$browser"; close(LOG); } } # prints out log and form to browser print "Content-type: text/html\n\n"; open (LOG, "$logpath"); @data = <LOG>; close(LOG); foreach $line(@data) { chomp($line); print "$line<br>"; } close(LOG); print <<EOT; <FORM ACTION="basic.pl" METHOD="POST"> <SELECT NAME="browser"> <OPTION VALUE="microsoft">microsoft</OPTION> <OPTION VALUE="netscape">netscape</OPTION> <OPTION VALUE="aol">aol</OPTION> <OPTION VALUE="mozilla">mozilla</OPTION> <OPTION VALUE="opera">opera</OPTION> </SELECT> <INPUT TYPE="SUBMIT" VALUE="Submit"> </FORM> EOT