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

I have a project coming up that has a rather short development cycle and a rather strict deadline. Most of the Perl scripts I've written manipulate ascii data from flat files and produce flat files or some sort of report. This new project is a bit different. I need to write a front end to a control file that is used by a couple of other processes. The idea is that a user would need to add, edit, and list(search) records from this control file. The file is made up of less than 1000 records that are : delimited. The records contain 21 fields and the combination of 2 of the fields(13 and 21) make a record unique. My plan is to open the file and add it to a hash that has the key(fields 13, 21) and the whole record as the value. Something like:

<psuedo-code>

#!/usr/bin/perl -w use strict; open(FH, "<control.txt") or die "yada... $!"; while (<FH>) { my $key = join "", (split/:/)[13,21]; %recs = ($key => $_); }
</psuedo-code>

This should give me an easy way to search for records.
This app will be run by a several users on widows terminal(reflection) software connected to our HP-UX server. I don't need record level locking, so I think an ascii flat file approach is best. If anyone can give me some suggestions regarding this project, particularly with respect to user interface or how you might go about handleing the records I'd appreciate it.

Replies are listed 'Best First'.
Re: Building a text dbm
by princepawn (Parson) on Oct 15, 2003 at 17:52 UTC
Re: Building a text dbm
by ptkdb (Monk) on Oct 15, 2003 at 18:22 UTC
    I've found that when working on something like this it's typically a good idea to create some sort of record for yourself up front. Perl has a marvelous afinity for producing such records out of heaps of data quickly. To embilish your pseudo code a bit:
    #!/usr/bin/perl -w use strict; my(@fields) = qw/field1 field2 .../ ; # all 21 of your fields CAREFULL +Y checked open(FH, "<control.txt") or die "yada... $!"; my($rec) ; while (<FH>) { @$rec{@fields} = split /:/ ; # hash slice into an auto-vivified has +h-reference (instant record) $recs{"$rec->{field13}$rec->{field21}"} = $rec ; } ## ## Individual records can be manipulated by field name ## $rec{keyOfInterest}->{fieldOfInterest} = NewValue ; ## ## to convert a rec back into a string: ## my($str) = join ":", @$rec{@fields} ;
Re: Building a text dbm
by Lhamo Latso (Scribe) on Oct 15, 2003 at 18:58 UTC

    I think this is doable with dbm, even if the keys are from 2 different parts of the record. I wrote an address book application that uses about 19 fields delimited to a dbm tied hash. Actually, it turned out to be a hash of arrays. Without the camel book chapter on Data Structures, I would have been lost, but they have good examples and diagrams to follow.

    I am including the subroutines here without the main program. The main section is specific to the application, whereas the subroutines are more generally usable to flatten out the hash-of-array structure into the key-value pairs needed for dbm output. I use ">" for the delimiter.

    use strict; use CGI qw(:standard :html3 escape unescape); use Apache::Constants qw(:common); use Apache::File (); use Apache::Util qw(ht_time escape_uri unescape_uri escape_html); use Fcntl qw( :DEFAULT :flock ); use POSIX qw(strftime); use DB_File; use vars qw(@FIELDS %REQUIRED $GUESTBOOKFILE %HoA); local *DBM; @FIELDS = qw(First_Name Last_Name Email_Address1 Email_Address2 Website Home_Phone Work_Phone Cell_Phone Other_Phone Street_Address1 Street_Address2 City State Zip Country Birthday Gender); sub write_guestbook { unless (TieLock($GUESTBOOKFILE, 1)) { print strong('Sorry, an error occurred: unable to open ' . $GUESTB +OOKFILE),p(); return; } my $i = param("HoAindex"); # check for an update unless ($i) { ## increment the sequencer. $HoA{sequence} = 0 unless exists $HoA{sequence}; $i = ++$HoA{sequence}; } my $date = strftime('%d-%b-%Y %R',localtime); $HoA{$i} = join(">", ($date), map {escape(param($_))} (@FIELDS)); unTieLock($GUESTBOOKFILE); return( "Thank you <b>". param('First_Name') ."</b>, for signing the guestbook."); } sub delete_guestbook { unless (TieLock($GUESTBOOKFILE, 1)) { print strong('Sorry, an error occurred: unable to open ' . $GUESTB +OOKFILE),p(); return; } my $i = param("HoAindex"); # get the delete index unless ($i) { print strong('Sorry, an error occurred: unable to get entry index +' . $GUESTBOOKFILE),p(); return; } delete $HoA{$i}; unTieLock($GUESTBOOKFILE); return( "Thank you <b>". param('First_Name') ."</b>, your delete has been completed."); } sub load_address { my $fname = param('First_Name'); my $lname = param('Last_Name'); my $return_msg; unless (TieLock($GUESTBOOKFILE, 0)) { print strong('Sorry, an error occurred: unable to open ' . $GUESTB +OOKFILE),p(); return; } my @data; for my $addr (sort keys %HoA) { @data = map {unescape($_)} split( ">", $HoA{$addr}, 18); $data[0] = $addr; #overlay the date with the key. last if ($fname eq $data[1]) && ($lname eq $data[2]); if ($data[0] eq "sequence") { @data = (); $return_msg = "Sorry <b>". param('First_Name') ."</b>, but your existing entry has not been foun +d."; } } unTieLock($GUESTBOOKFILE); return ($return_msg, @data); } sub view_guestbook { my $guestbook; my @rows; unless (TieLock($GUESTBOOKFILE, 0)) { print strong('Sorry, an error occurred: unable to open ' . $GUESTB +OOKFILE),p(); return; } for my $addr (sort keys %HoA) { next if $addr eq "sequence"; my @data = map {unescape($_)."&nbsp;"} split( ">", $HoA{$addr}, 1 +8); unshift @rows, td(\@data); } unshift @rows, th(['Entry Date',@FIELDS]); $guestbook .= p( table({-border=>'1', -hspace=>'5', -cellspacing=>'1', -cellpadding=>'4'}, TR(\@rows))); unTieLock($GUESTBOOKFILE); return ($guestbook); } sub TieLock { # tie and lock the dbm file. my $path = shift; my $for_writing = shift; my $lock_type; if ($for_writing) { $lock_type = LOCK_EX; } else { $lock_type = LOCK_SH; } my $db = tie %HoA, 'DB_File', $path, O_RDWR | O_CREAT, 0666, $DB_HA +SH or die "[Error processing $path ] $!"; my $fd = $db->fd; open DBM, "+<&=$fd" or die "Could not dup DBM for lock: $!"; # now try to lock it my $success; my $tries = 0; while ($tries++ < 10) { last if $success = flock (DBM, $lock_type|LOCK_NB); print p("Waiting for $lock_type lock on AddressBook file..."); sleep(1); # wait a second } undef $db; unless ($success) { warn("Couldn't get lock for $lock_type"); return; } return 1; } sub unTieLock { # untie the dbm file. untie %HoA; close DBM; }