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

I am trying to print out the records in a flatfile database, with the results in alphabetical order, based on the first 8 characters in the first field, which is lname (last name). This is what I have so far (doesnt work).
sub view_it { open (DATABASE, "$mydata") || &CgiDie ("I am sorry, but I was not able + to open the data file."); flock (DATABASE, 2); @list = <DATABASE>; flock (DATABASE, 8); close (DATABASE); foreach $list (@list) { ($lname, $fname, $email, $phone, $message, $ip, $date) = split(/\|\|/, + $list); &byName; $test = 1; &print_it; } } sub byName { # sort database by first 8 of last name ((substr $a, 0, 8) cmp (substr $b, 0, 8)); } $i = 0; # loop counter while (<>) { $filerecs[$i++] = $_; } @sorted_recs = sort byName @filerecs
then it goes on to substitute the $lname for %%name%% etc, and print it to a html template (which works fine). Problem is, the script prints in reverse entry order instead of alphabetical. Any suggestions for a new kid that is really lost. ? Shay

Edit kudra, 2001-10-30 Changed title

  • Comment on Where am I going wrong with printing flatfile db records in alphabetical order?
  • Download Code

Replies are listed 'Best First'.
Re: Where am I going wrong ?
by davorg (Chancellor) on Oct 29, 2001 at 19:44 UTC

    To be honest, your code really doesn't make much sense. Here's how I'd do it:

    open DATABASE, $mydata or die "Can't open $mydata: $!\n"; flock DATABASE, LOCK_EX or die "Can't lock $mydata: $!\n"; my @list = <DATABASE>; close DATABASE; # which also unlocks it @list = sort { substr($a, 0, 8) cmp substr($b, 0, 8) } @list;

    At the end of this code, @list contains the data in sorted order.

    Update: Ovid is right, of course. Corrected.

    --
    <http://www.dave.org.uk>

    "The first rule of Perl club is you don't talk about Perl club."

      If you need the first eight characters, I believe you want substr($foo, 0, 8) and not substr($foo, 8, 0). :)

      Cheers,
      Ovid

      Join the Perlmonks Setiathome Group or just click on the the link and check out our stats.

Re: Where am I going wrong ?
by Albannach (Monsignor) on Oct 29, 2001 at 19:51 UTC
    You need to actually call sort if you want something sorted ;-)

    You need to sort the whole @list and then loop through to print if that's what you're after. Since you are sorting by the first field anyway, why bother with the 8 character restriction? I'd think that just adding @list = sort @list; before your printing loop should do what you want.

    --
    I'd like to be able to assign to an luser

      Thanks, this worked:
      @list = sort @list;

        Not to beat a dead horse too much, but is there any reason not to simply sort the list as it's read (which conserves memory, if THAT matters here)? To wit:

        open DATABASE, $mydata or die "Can't open $mydata: $!\n"; flock DATABASE, LOCK_EX or die "Can't lock $mydata: $!\n"; my @list = sort <DATABASE>; # NOTE ^^^^ close DATABASE; # which also unlocks it

        dmm

        
        You can give a man a fish and feed him for a day ...
        Or, you can teach him to fish and feed him for a lifetime
        
Re: Where am I going wrong ?
by jbert (Priest) on Oct 30, 2001 at 14:03 UTC
    Hmm. Any particular reason to only sort on the first 8 characters of the last name? Unless your names are really really long (and perhaps not even then) I don't think it will make a difference to your output if you sort on the entire last name. That way you can avoid the custom sort routine.

    Some other minor nits to pick:

    • You call to 'flock', presumably to make sure that the database is in a consistent state when you read it. Fair enough. Make sure, though, that you read the help for 'flock' (perldoc -f flock). This mentions that the locking is advisory and so all pieces of software which interact with the DB must call flock or the one which doesn't won't even notice the locks which the others hold.
    • Your use of "magic numbers" '2' and '8' jarred enough to make me look up the constants. '2' is LOCK_EX and '8' is LOCK_UN, which I believe are 'exclusive lock' and 'unlock'. Since you are only reading the database you probably don't need an exclusive lock, a shared lock would do - and would allow multiple readers.
      If your code ends up getting used in a CGI script an exclusive lock would mean that only one instance at a time could read the file - an interesting performance bug for the maintainer to track down in 12 months time when the file is huge - made much worse by using numbers and not names for the parameters.