Multidimensional array

P

Paul E. Schoen

I am trying to construct an array (or possibly a hash) which consists of
arrays. It is organized as a series of records with four text fields:

Date, Time, Title, Description

I was able to get this to work with the following ugly but functional code:

my @E_Dates = ("20100831", "20100910");
my $E_Time = "14:00"; #Time in hh:mm format
my $E_Title = "Example";
my $E_Descr = "Line 1 \nLine2";

my $Event_DB = [[$E_Dates[0], $E_Time, $E_Title, $E_Descr]];

$E_Time = "18:00"; #Time in hh:mm format
$E_Title = "Example Title 2";
$E_Descr = "Example 2 Line 1 \nExample 2 Line2";

@E_Details = ($E_Time, $E_Title, $E_Descr); # Array of three elements

$Event_DB->[1] =[ $E_Dates[1], $E_Time, $E_Title, $E_Descr];

print "$Event_DB->[0][0]"; #prints first Date
print "$Event_DB->[0][1]"; #prints first Time
print "$Event_DB->[0][2]"; #prints first Title
print "$Event_DB->[0][3]"; #prints first Descr

print "$Event_DB->[1][0]"; #prints second Date
print "$Event_DB->[1][1]"; #prints second Time
print "$Event_DB->[1][2]"; #prints second Title
print "$Event_DB->[1][3]"; #prints second Descr

But when I tried to use an array, I could not use the two dimensional
indexes:

my @E_Dates = ("20100831", "20100910"); #Date in yyyymmdd format
my $E_Time = "14:00"; #Time in hh:mm format
my $E_Title = "Title1";
my $E_Descr = "Descr1-Line1 \nDescr1-Line2";

my @Event_DB = ($E_Dates[0], $E_Time, $E_Title, $E_Descr);

$E_Time = "18:00"; #Time in hh:mm format
$E_Title = "Title2";
$E_Descr = "Descr2-Line1 \nDescr2-Line2";

push( @{Event_DB}, $E_Dates[1], $E_Time, $E_Title, $E_Descr );

for (my $i=0; $i<@Event_DB; $i+=4) {
print "<p>";
print "<h3>Title $i: $Event_DB[$i+2]</h3>\n";
print "<br><h4>Date: $Event_DB[$i]</h4>\n";
print "<br><h4>Time: $Event_DB[$i+1]</h4>\n";
print "<br><h5>Description: $Event_DB[$i+3]</h5></p><hr>\n";
}

I tried everything I could find and I had no success. I originally tried to
create a hash with the date as the index and an array for the data, about
like this:

my @E_Dates = ("20100831", "20100910");
my $E_Time = "14:00"; #Time in hh:mm format
my $E_Title = "Example";
my $E_Descr = "Line 1 \nLine2";
# Hash with Date as sorting key, Array of Details
my %Event_DB = (Date=>$E_Dates[0], Details=>[$E_Time, $E_Title,
$E_Descr]);
$E_Time = "18:00"; #Time in hh:mm format
$E_Title = "Example Title 2";
$E_Descr = "Example 2 Line 1 \nExample 2 Line2";
@E_Details = ($E_Time, $E_Title, $E_Descr); # Array of three elements
# Adding second record with push
push @{ $Event_DB{Date=>$E_Dates[1]}}, Details=>[$E_Time, $E_Title,
$E_Descr];
# Alternate method (probably very wrong)
%Event_DB(Date=>$E_Dates[1]) = Details=>[$E_Time, $E_Title, $E_Descr];

I would like to be able to sort the array or hash by date and then format it
to be printed as HTML. I am more familiar with C and Borland Delphi Pascal
where this would be simple. It's probably also simple in Perl but I can't
figure out how to do it.

TIA,

Paul
 
W

Wolf Behrenhoff

I am trying to construct an array (or possibly a hash) which consists of
arrays. It is organized as a series of records with four text fields:

Date, Time, Title, Description

I was able to get this to work with the following ugly but functional code:

my @E_Dates = ("20100831", "20100910");
my $E_Time = "14:00"; #Time in hh:mm format
my $E_Title = "Example";
my $E_Descr = "Line 1 \nLine2";

my $Event_DB = [[$E_Dates[0], $E_Time, $E_Title, $E_Descr]];

$E_Time = "18:00"; #Time in hh:mm format
$E_Title = "Example Title 2";
$E_Descr = "Example 2 Line 1 \nExample 2 Line2";

@E_Details = ($E_Time, $E_Title, $E_Descr); # Array of three elements

$Event_DB->[1] =[ $E_Dates[1], $E_Time, $E_Title, $E_Descr];

print "$Event_DB->[0][0]"; #prints first Date
print "$Event_DB->[0][1]"; #prints first Time
print "$Event_DB->[0][2]"; #prints first Title
print "$Event_DB->[0][3]"; #prints first Descr

It's a bad idea to store different things (Date, Time, Title, Descr) in
the same array. What happens if you try to read the code next week? Do
you still know that index 3 is the description? So it is probably better
to use a hash here and use "Date", ... as key. Depending on what you
plan to store here, it might be even better to use OO and create a class
which encapsulates this dataset (see perldoc perltoot). With the OO
approach, also your array of arrays goes away, you will only have a
simple array of event objects.

Furthermore I would suggest to store date and time together in a
timestamp. Convert it to your favorite format (hh:mm or whatever) just
before you display it - that makes a lot of things easier (for example,
how would you add 90 minutes to "23:00"? What about time zones?).

Wolf
 
C

ccc31807

I am trying to construct an array (or possibly a hash) which consists of
arrays. It is organized as a series of records with four text fields:

Date, Time, Title, Description

Paul, see the script below and output.

With this kind of task, you first need to consider the format of your
input and output. You want output to be HTML, and I have done so. I
have assumed that your data will be in CSV format, but of course that
depends on your app and in any case is easy enough to change.

It's much, much easier when working with data of this kind to name the
fields rather than use positional notation, which means that you will
use hashes of hashes (or hashes of hash refs to be more accurate).
Accordingly, I have named your fields date, time, title, and desc, and
have used 'date' as the key. Here is a script and output.

------------------script---------------
#! perl
# event_calendar.plx

use strict;
use warnings;

my %events;

while (<DATA>)
{
next unless /\w/;
chomp;
my ($date, $time, $title, $desc) = split /,/;
$desc =~ s!\\n!<br />!g;
$events{$date}{time} = $time;
$events{$date}{title} = $title;
$events{$date}{desc} = $desc;
}

foreach my $date (sort keys %events)
{
print qq(
<h1>Date: $date</h1>
<ul>
<li>Time: $events{$date}{time}</li>
<li>Title: $events{$date}{title}</li>
<li>$events{$date}{desc}</li>
</ul>);
}

exit(0);

__DATA__
20100831,14:00,Example,Line 1\nLine 2
20100910,18:00,Example Title 2,"Example 2 Line 1\nExample 2 Line 2
17760704,12:00,Revolution!,Break away from Britian\nForm new republic

-------------output-------------------

<h1>Date: 17760704</h1>
<ul>
<li>Time: 12:00</li>
<li>Title: Revolution!</li>
<li>Break away from Britian<br />Form new republic</li>
</ul>
<h1>Date: 20100831</h1>
<ul>
<li>Time: 14:00</li>
<li>Title: Example</li>
<li>Line 1<br />Line 2</li>
</ul>
<h1>Date: 20100910</h1>
<ul>
<li>Time: 18:00</li>
<li>Title: Example Title 2</li>
<li>"Example 2 Line 1<br />Example 2 Line 2</li>
</ul>
 
P

Paul E. Schoen

Ben Morrow said:
You are confused about the difference between lists and arrayrefs. You
could rewrite this example so it worked correctly by changing these
lines:

# This creates an array containing a *single* element, which happens
# to be an arrayref.
my @Event_DB = [$E_Dates[0], $E_Time, $E_Title, $E_Descr];

#...

# This pushes another *single* element, which again happens to be an
# arrayref.
push @Event_DB, [$E_Dates[1], $E_Time, $E_Title, $E_Descr];

# Now you have a proper 2D array:
for my $i (0..$#Event_DB) {
print <<HTML;
<p>
<h3>Title $i: $Event_DB[$i][2]</h3>
<br><h4>Date: $Event_DB[$i][0]</h4>
...
HTML
}

You might rather store your records as hashrefs (so you can name the
fields instead of numbering them), in which case you want

my %Event_DB = (
$E_Dates[0] => {
Date => $E_Dates[0],
Time => $E_Time,
Title => $E_Title,
Descr => $E_Descr,
},
);

(Notice that I put the date in the record even though it was already
stored as the key. This is usually a good idea, as it makes processing
simpler later.)

$Event_DB{$E_Dates[1]} = [$E_Time, $E_Title, $E_Descr];

Using the first model (array-of-arrayrefs):

for my $event (sort { $a->[0] cmp $b->[0] } @Event_DB) {
print <<HTML;
<p>Date: $event->[0]
<p>Title: $event->[2]
HTML
}

Using the second (hash-of-arrayrefs):

for my $date (sort keys %Event_DB) {
print <<HTML;
<p>Date: $date
<p>Title: $Event_DB{$date}[2]
HTML
}

Using the third (hash-of-hashrefs):

for my $event (map $Event_DB{$_}, sort keys %Event_DB) {
print <<HTML;
<p>Date: $event->{Date}
<p>Title: $event->{Title}
HTML
}

Have you read perldoc perldsc, perldoc perllol and perldoc perlreftut?

I have downloaded the entire set of documentation, and I see that there is a
lot to learn. But as it explains in the tutorial, perhaps only 10% needs to
be understood and used. It is confusing (to me) and apparently there are
multiple ways to accomplish the same thing (and this is also true in C and
other languages). I find it hard to allow the interpreter to take care of
things that would need to be more formally expressed in a strongly typed
language.

Thanks for your help. I have made the simple changes you showed and the
script works. Now I need to look at the overall design of the project and
write the proper code to do what I want it to do. For now I think I can get
by with a simple text file for the array of events, adding, sorting,
inserting, deleting, and editing as needed, and then convert to HTML for use
in my home page.

Eventually, something like MySQL might be useful, but that's another
learning curve.

I also appreciate the replies from others who have given me other
perspectives on how to proceed.

Paul
 
U

Uri Guttman

PES> I have downloaded the entire set of documentation, and I see that
PES> there is a lot to learn. But as it explains in the tutorial, perhaps

why did you download it? if you have perl installed properly, you
already have the docs! this is a core perl skill to use, know where the
docs are, how to scan them and learn what they cover. there are many
perl tutorials in the docs today as well.

PES> only 10% needs to be understood and used. It is confusing (to me) and
PES> apparently there are multiple ways to accomplish the same thing (and
PES> this is also true in C and other languages). I find it hard to allow
PES> the interpreter to take care of things that would need to be more
PES> formally expressed in a strongly typed language.

why? the whole idea is for perl to do the work for you. it means less
coding and fewer bugs on your part. that is more important than how much
cpu is used.

PES> Thanks for your help. I have made the simple changes you showed and
PES> the script works. Now I need to look at the overall design of the
PES> project and write the proper code to do what I want it to do. For now
PES> I think I can get by with a simple text file for the array of events,
PES> adding, sorting, inserting, deleting, and editing as needed, and then
PES> convert to HTML for use in my home page.

you should really switch to a hash for the lower level. arrays of
different types will break eventually. you can't add/delete something
without changing ALL the uses of those integers later on. with a hash
you can change all you want and the existing code will always work (or
require much less changing than with arrays). hashes are the key perl
construct, arrays are usually secondary.

uri
 
P

Paul E. Schoen

Uri Guttman said:
PES> I have downloaded the entire set of documentation, and I see that
PES> there is a lot to learn. But as it explains in the tutorial, perhaps

why did you download it? if you have perl installed properly, you
already have the docs! this is a core perl skill to use, know where the
docs are, how to scan them and learn what they cover. there are many
perl tutorials in the docs today as well.

I am working on a Windows Vista machine and Perl is installed on the server.
I use Telnet to access my web space, assign permissions, run test scripts,
and sometimes I even use the pico editor. But I'm using PerlEdit locally to
edit the scripts and HTML/JavaScript files, and I use NCH Classic FTP for
uploads. It's handy to have the docs available off-line.

For developing Windows GUI apps with Borland Delphi I am spoiled by having a
context sensitive help system available and also a sophisticated debugger.
It would be great to have tools like that for Perl. I suppose I could use
the debugging features but I would probably need to run the script from the
command line.
PES> only 10% needs to be understood and used. It is confusing (to me)
and
PES> apparently there are multiple ways to accomplish the same thing (and
PES> this is also true in C and other languages). I find it hard to allow
PES> the interpreter to take care of things that would need to be more
PES> formally expressed in a strongly typed language.

why? the whole idea is for perl to do the work for you. it means less
coding and fewer bugs on your part. that is more important than how much
cpu is used.

Perl does seem to be very powerful, but the syntax is difficult for me to
grasp because it is so cryptic. I am used to language that is verbose and
highly readable, while it seems like a rite of passage to be able to read
the various symbols that constitute much of Perl. Of course, it is a
learning curve, and as I build my script I am learning what they mean. I
don't have the time or enthusiasm to learn Perl in a structured manner as a
course in the language. I intend to learn enough to accomplish my immediate
objectives, and then move on to other things. I am primarily a hardware
engineer specializing in high power test equipment, which often involves PIC
programming which is mostly assembly language and some C. So I am more
comfortable with low level programming at the bit and byte and memory
address level. Delphi provides a means for OOP and strict type declarations
which IMHO makes for a more readable source and less chance for bugs.
PES> Thanks for your help. I have made the simple changes you showed and
PES> the script works. Now I need to look at the overall design of the
PES> project and write the proper code to do what I want it to do. For
now
PES> I think I can get by with a simple text file for the array of
events,
PES> adding, sorting, inserting, deleting, and editing as needed, and
then
PES> convert to HTML for use in my home page.

you should really switch to a hash for the lower level. arrays of
different types will break eventually. you can't add/delete something
without changing ALL the uses of those integers later on. with a hash
you can change all you want and the existing code will always work (or
require much less changing than with arrays). hashes are the key perl
construct, arrays are usually secondary.

I think I will use the hash. And I will probably use a DateTime variable
type for the key. And I will certainly use names for references rather than
integer array indexes, but I was so confused that I wanted to simplify as
much as possible, just to get something working.

BTW, I have tried adding comments to the following code which was part of
the original mailer.pl I used as a starting point. I think I understand most
of it but there are some areas that seem strange:

foreach ('Full_Name', 'Email', 'Event_Title','Event_Date','Event_Time',
'Event_Description') {

# If a field has newlines, it's probably a block of text; indent it.
# Binary "=~" binds a scalar expression to a pattern match.
# Search $in{$_} for \n newline
if ($in{$_}=~ /\n/) {
# Catenation "." add \n newline ahead of data string
$in{$_}= "\n" . $in{$_} ;
# Substitute \n with \n + four spaces, /g = global, all instances
$in{$_}=~ s/\n/\n /g ;
# Concatenate \n newline at end
$in{$_}.= "\n" ;
}

# comma-separate multiple selections
#************* What is \0 except a null? ***************
$in{$_}=~ s/\0/, /g ; # Substitute \0 with comma and one space ????

# Print fields, aligning columns neatly
# %-8s is 8 character width left justified for $_: (Label+':'),
# followed by $in{$_} (Data) and a newline
printf MAIL "%-${maxlength}s %s\n", "$_:", $in{$_} ;
}

I think I understand it except for the \0. Can a null character appear in
CGI data?

Thanks,

Paul
 
R

Randal L. Schwartz

Paul> I am working on a Windows Vista machine and Perl is installed on
Paul> the server. I use Telnet to access my web space,

Seriously? Telnet? /me looks at calendar

In *September* of *2010*?

/me rolls eyes

print "Just another Perl hacker,"; # the original
 
J

Jürgen Exner

Paul E. Schoen said:
I am working on a Windows Vista machine and Perl is installed on the server.
I use Telnet to access my web space, assign permissions, run test scripts,
and sometimes I even use the pico editor. But I'm using PerlEdit locally to
edit the scripts and HTML/JavaScript files, and I use NCH Classic FTP for
uploads. It's handy to have the docs available off-line.

It is even handier to have a Perl installation available off-line.
Actually, coming to think of it, it is quite stupid to do any
development work on a live web server.

jue
 
P

Paul E. Schoen

Jürgen Exner said:
It is even handier to have a Perl installation available off-line.
Actually, coming to think of it, it is quite stupid to do any
development work on a live web server.

I used the ActivePerl installation as suggested by Sherm and it works well.
However, I can't seem to get the proper URL to the Perl script on my local
machine to be used by the HTML event submission form.

And I also need to add the path to the C:\Perl\bin folder. I can do it from
the command line by using:

set path=%path%;C:\Perl\bin

but it is only temporary.

I agree that the local machine is the place to do the development work.

The project is coming along pretty well now. Thanks for the help!

Paul
 
J

Jürgen Exner

Paul E. Schoen said:
I used the ActivePerl installation as suggested by Sherm and it works well.
However, I can't seem to get the proper URL to the Perl script on my local
machine to be used by the HTML event submission form.

That depends on how you configurated your web server.
And I also need to add the path to the C:\Perl\bin folder. I can do it from
the command line by using:

set path=%path%;C:\Perl\bin

but it is only temporary.

Start menu -> right-click on "Computer" -> Properties -> Advanced System
Settings -> Environment Variables

But normally ActiveState will add the directory to your path
automatically upon installation.

jue
 
C

ccc31807

It is even handier to have a Perl installation available off-line.
Yes.

Actually, coming to think of it, it is quite stupid to do any
development work on a live web server.

This depends. I do most of my work on servers, to which I connect with
a terminal emulator, or in the case of Windows with a remote desktop.
Some of these servers run httpd servers, along with other kinds of
servers such as database servers ftp servers, email servers, file
servers, and so on. If you develop on remote machines, you often don't
have any control on what servers run on those machines, and the fact
that a web server is running on a particular remote machine really
doesn't impact your development environment.

Too, you can develop applications on test servers not connected to the
internet, but accessible internally via TCP/IP, with the express
intention of developing on a 'live' web server. In fact, testing on a
'live' web server is mandatory!

This doesn't even include considerations such as developing of virtual
machines.

I agree that it probably isn't best to develop 'live' in the sense
that your in process code is available live over a WWW connection, but
this isn't the same as developing on a 'live' web server.

CC.
 
C

ccc31807

Once again, you're assigning new meaning to terms that disagree with
the meaning used by the rest of us, and then trying to argue that it's
everyone else's meaning that is wrong. Please stop doing that.

Not so, I merely take words in their literal meaning unless they are
obviously meant to be taken non literally. JUE said,

I take two things from this: (1) the use of the term 'any development
work' isn't limited to web apps, and it's common in my experience to
develop data munging apps on a machine that also happens to be running
a 'live' web server (unless you are in a Windows environment where you
use one piece of hardware for one software server), and (2) it's also
common in my experience to develop web apps outside the document root,
test them, and then copy them to the document root (or configure
Apache to serve them). I've contracted out three jobs in the past
year, all on remote servers, and in each case the machine I was hired
to work on was running a 'live' web server, and I never felt that it
was stupid to work on the same machine -- but in none of the cases was
I actually working on a live web site but ancillary applications.
A "live" server is what is also called a "production" or "deployment"
server. The fact that your "staging," "development," or "test" servers
happen to run httpd and are accessible via TCP/IP does not imply that
they are "live" - that's not what the word means in this context.

But he didn't say, "it is quite stupid to do web development work on a
production or deployment web server." If he had meant to say that, I'm
sure he would. That's what you understood him to say, but it's not
what he actually said. It seems to be that you have made some
assumptions that weren't explicity stated in JUE's statement and are
taking me to task because I don't share your assumptions.

Besides, I also said,

which makes clear the meaning that you have untested and in-process
code in your document root for all to see. (We all do stupid things,
and I confess that I have been guilty of developing web apps on live
production servers fully connected to the internet, but only when told
to do so by a client who gave these instructions.)

CC.
 
P

Paul E. Schoen

Jürgen Exner said:
That depends on how you configurated your web server.

I have a localhost web server that was installed by DotCMS. But it only
displays the default home page and items linked to it. There is a place for
"Vanity URLs" that supposedly redirect an arbitrary URL to another location,
but I can't seem to get that to work. I think it needs to redirect to the
files in the local website, rather than other files on the local machine
hard drive. The files in the DotCMS directory are very confusing and I can't
find folder names that work in a URL, such as http://localhost/news-events.
The only place news-events appears is deep into a path:

C:\dotcms\dotcms\dotsecure\dotcache\livecache.fdb\9\0\7\2\livecache48190c8c-42c4-46af-8d1a-0cd5db894797_colon__forward_news-events_forward_.fdb

Maybe I should uninstall the DotCMS host and install an Apache server?
Start menu -> right-click on "Computer" -> Properties -> Advanced System
Settings -> Environment Variables

But normally ActiveState will add the directory to your path
automatically upon installation.

I may have missed that checkbox when I installed it. Thanks for the clue.
Now I can run Perl from the command line anywhere.

I'm stuck once again. I want to be able to store a hash of events sorted by
Date (or perhaps DateTime) in a text file, and read back into the hash where
I can add new events or delete old ones. The hash is defines as:

my %Event_DB = (
$E_Date => {
Date => $E_Date, #This was previously an array
Time => $E_Time,
Title => $E_Title,
Descr => $E_Descr,
},
);

I am storing the data received from the HTML form as follows:

my $filename = "events.txt";

#*** Can't create file in cgi-bin; must create empty file and chmod 777
#unless (-e $filename) { #check for exist
open (EventFile, '>>', $filename)
or HTMLdie ("File write error: $!");

for my $event (map $Event_DB{$_}, sort keys %Event_DB) {
print (EventFile $event->{Title});
print (EventFile $event->{Date});
print (EventFile $event->{Time});
print (EventFile $event->{Descr});
}
close (EventFile);
# }

This seems to work, although there are no field or record separators. The
Descr record may contain newlines, so I may need to use other characters for
separators.

I am reading the stored Events like this (which must be wrong);

# Read the existing text file of events
open (EventFile, '<', $filename)
or HTMLdie ("File read error: $!");
my $Event_DB = <EventFile>;
close (EventFile);

I am writing to the HTML file as follows:

# Print to the HTML file (write, append, create)
open (DATA1, '>', "output.htm")
or HTMLdie ("File error: $!");

print DATA1 <<EOF ;
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<title>iframe - Events - Greater Baltimore Group Sierra Club Web
Site</title>
</head>
<body>
<h1>Events</h1><hr>
EOF

foreach my $key (sort keys %Event_DB) {
print DATA1 "<p><h3>$Event_DB{$key}->{Title}</h3>";
print DATA1 "<h4>Date: $Event_DB{$key}->{Date} Time:
$Event_DB{$key}->{Time}</h4>";
print DATA1 "$Event_DB{$key}->{Descr}</p><hr>";
}

print DATA1 <<EOF ;
</body>
</html>
EOF

close (DATA1);

I think my main problem is the saving and reading of the events.txt file.
The here-doc and the print loop print the fields of the last record, but
there is only one execution of the loop.

Surely something like this has been done before, but I don't know how to
find a well-written Perl script for this. I found this, which uses a
database file, and may be the best option:

http://docstore.mik.ua/orelly/perl/cookbook/ch11_15.htm

use MLDBM qw(DB_File);
use Fcntl;
tie(%hash, 'MLDBM', 'testfile.db', O_CREAT|O_RDWR, 0666)
or die "can't open tie to testfile.db: $!";

# ... act on %hash

# this doesn't work!
$hash{"some key"}[4] = "fred";

# RIGHT
$aref = $hash{"some key"};
$aref->[4] = "fred";
$hash{"some key"} = $aref;

untie %hash;

I think I'll try that, but I'd appreciate a few more tips on how to do what
I need in the "best" way.

Thanks muchly,

Paul
 
U

Uri Guttman

PES> Maybe I should uninstall the DotCMS host and install an Apache server?

yes, yes, and YES. no reason to use anything but apache. you can do so
many things on your own and not need your external web host for anything
but production.

PES> I'm stuck once again. I want to be able to store a hash of events
PES> sorted by Date (or perhaps DateTime) in a text file, and read back
PES> into the hash where I can add new events or delete old ones. The hash
PES> is defines as:


PES> my %Event_DB = (
PES> $E_Date => {
PES> Date => $E_Date, #This was previously an array
PES> Time => $E_Time,
PES> Title => $E_Title,
PES> Descr => $E_Descr,
PES> },
PES> );

PES> I am storing the data received from the HTML form as follows:

PES> my $filename = "events.txt";

PES> #*** Can't create file in cgi-bin; must create empty file and chmod 777
PES> #unless (-e $filename) { #check for exist
PES> open (EventFile, '>>', $filename)

use a lexical handle for that. safer.

open( my $event_file .... )

PES> or HTMLdie ("File write error: $!");

PES> for my $event (map $Event_DB{$_}, sort keys %Event_DB) {

maybe simpler and cleaner this way:

foreach my $event_date ( sort keys %Event_DB) {

my $event = $Event_DB{$event_date} ;

PES> print (EventFile $event->{Title});
PES> print (EventFile $event->{Date});
PES> print (EventFile $event->{Time});
PES> print (EventFile $event->{Descr});

no need for multiple prints. do that like this:

print $event_file $event->{Title},
$event->{Date}, $event->{Time}, $event->{Desc} ;

but there is nothing separating those values so how will you parse them
out when you read it? i dunno the whole program so maybe there is
something i didn't see. you can so a simple join on tab or , and make a
trivial CSV file. a slice works well here:

print $event_file
join( "\t", @{$event}{qw( Title Date Time Desc)} ), "\n" ;

PES> foreach my $key (sort keys %Event_DB) {

if they were sorted when written to the file, you don't need to sort
again. choose one place to sort them.

i didn't go over the remaining code or your bug as i don't have the time
right now.

uri
 
J

Jim Gibson

Paul E. Schoen said:
I'm stuck once again. I want to be able to store a hash of events sorted by
Date (or perhaps DateTime) in a text file, and read back into the hash where
I can add new events or delete old ones. The hash is defines as:

my %Event_DB = (
$E_Date => {
Date => $E_Date, #This was previously an array
Time => $E_Time,
Title => $E_Title,
Descr => $E_Descr,
},
);

I am storing the data received from the HTML form as follows:

my $filename = "events.txt";

#*** Can't create file in cgi-bin; must create empty file and chmod 777
#unless (-e $filename) { #check for exist
open (EventFile, '>>', $filename)
or HTMLdie ("File write error: $!");

for my $event (map $Event_DB{$_}, sort keys %Event_DB) {
print (EventFile $event->{Title});
print (EventFile $event->{Date});
print (EventFile $event->{Time});
print (EventFile $event->{Descr});
}
close (EventFile);
# }

This seems to work, although there are no field or record separators. The
Descr record may contain newlines, so I may need to use other characters for
separators.

I presume you have good reason for writing the hash out to a file and
reading it back later. As Uri pointed out, your fields and records are
run together because you have printed them out without any separators.

If one of your fields can have newlines, then how are you going to
determine the end of the record?
I am reading the stored Events like this (which must be wrong);

# Read the existing text file of events
open (EventFile, '<', $filename)
or HTMLdie ("File read error: $!");
my $Event_DB = <EventFile>;

That reads one line from the input file (which may have all the records
in it if it was written by the code shown earlier). If you want to read
all of the records in the file, you need a loop:

while( my $line = <EventFile> ) {
chomp($line);
...
}

You are putting data from the file into the hash. The $Event_DB is a
scalar variable that contains the first line of the file. The %Event_DB
is a hash variable. The contents of the two are not related, and what
is read into $Event_DB will not automatically show up in %Event_DB.
close (EventFile);

I am writing to the HTML file as follows:

# Print to the HTML file (write, append, create)
open (DATA1, '>', "output.htm")
or HTMLdie ("File error: $!");

print DATA1 <<EOF ;
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<title>iframe - Events - Greater Baltimore Group Sierra Club Web
Site</title>
</head>
<body>
<h1>Events</h1><hr>
EOF

foreach my $key (sort keys %Event_DB) {
print DATA1 "<p><h3>$Event_DB{$key}->{Title}</h3>";
print DATA1 "<h4>Date: $Event_DB{$key}->{Date} Time:
$Event_DB{$key}->{Time}</h4>";
print DATA1 "$Event_DB{$key}->{Descr}</p><hr>";
}

print DATA1 <<EOF ;
</body>
</html>
EOF

close (DATA1);

I think my main problem is the saving and reading of the events.txt file.
The here-doc and the print loop print the fields of the last record, but
there is only one execution of the loop.

Well your original hash only contains one element. The code that
appends data to the file doesn't contain any newlines, so you will
never have more than one line. Have you looked at the file? Does it
have more than one line? Can you show us the code where you
Surely something like this has been done before, but I don't know how to
find a well-written Perl script for this. I found this, which uses a
database file, and may be the best option:

http://.../orelly/perl/cookbook/ch11_15.htm

Are you aware that may be an illegal excerpt of a copyrighted book?
use MLDBM qw(DB_File);
use Fcntl;
tie(%hash, 'MLDBM', 'testfile.db', O_CREAT|O_RDWR, 0666)
or die "can't open tie to testfile.db: $!";

# ... act on %hash

# this doesn't work!
$hash{"some key"}[4] = "fred";

# RIGHT
$aref = $hash{"some key"};
$aref->[4] = "fred";
$hash{"some key"} = $aref;

untie %hash;

That approach is for a big hash (>100,000) containing references. Since
you don't seem to have that problem, you can just use the Storable
module to save a hash and restore it later.

See 'perldoc Storable':

use Storable;
store( \%tEvent_DB, $filename);

Later:

%Event_DB = %{ retrieve($filename) };
 
J

Jürgen Exner

[Losts of irrelevant rambling about different kinds of servers snipped.
Nobody is questioning that doing developement for service X on a machine
that happens to run service Y would most likely not impact service Y]
Too, you can develop applications on test servers not connected to the
internet, but accessible internally via TCP/IP,

Absolutely. Developing on a test server has significant advantages over
developing on a live server:
- If you make a mistake you are taking down only the test server, not
the live server with maybe hundreds of thousands of customers
- It is far less likely that crackers can break into the system due to
security holes in the unfinished code, because the system is not open to
the public
- Even if they do manage to break in the rewards are meager: they only
get access to mockup data instead of PII, financial, confidential and
other critical customer data
- You can try your code without risking to majorly screw up your live
customer data
- You can try unusual and rare data combinations without messing up your
live data, e.g. have the fictitious visiting business man in Bangkok
enter his name in Hangul and his current location in full lenght Thai
(http://en.wikipedia.org/wiki/Bangkok#Full_name).

Still, it has some serious drawbacks:
- the test team will be upset because you are changing their test
environment constantly. Therefore there is no way to create a reliable
repro scenario
- management will be upset because the vast majority of bugs found will
be of the kind "Server Error 500: Syntax error in line 42", which could
have been found much much cheaper by running "perl -c" instead of having
a human actually call the site in a browser.
- everyone will be upset because they are blocked from doing anything
until you fix that syntax error. And even worse, nobody knows if you are
actively working on a fix, are taking an urgent leak, or have gone home
for a 2 week vacation.
with the express
intention of developing on a 'live' web server.

???
How is developing on a test server related to developing on a live
server? In most critical aspects like who is impacted if something goes
wrong those are two totally different animals.
In fact, testing on a
'live' web server is mandatory!

Yes, after you have gone through the full test pass on your test server
you should definitely do an additional test pass or at the very least an
immediate sanity check after you deployed to your live server.
This doesn't even include considerations such as developing of virtual
machines.

If virtual or physical doesn't make any difference.
I agree that it probably isn't best to develop 'live' in the sense
that your in process code is available live over a WWW connection, but

Oh, so we are in agreement that developing on a live server is a bad
idea? Great!
this isn't the same as developing on a 'live' web server.

?????

What? For all intent and purpose the live server is connected to and
accessible from the Internet. How else would your customers be able to
reach it (ignoring e.g. private or internal corporate networks for the
moment)?

jue
 
J

Jürgen Exner

ccc31807 said:
Not so, I merely take words in their literal meaning unless they are
obviously meant to be taken non literally. JUE said,


I take two things from this: (1) the use of the term 'any development
work' isn't limited to web apps,

In the context of this thread obviously we are discussing CGI
development.
I've contracted out three jobs in the past
year, all on remote servers, and in each case the machine I was hired
to work on was running a 'live' web server, and I never felt that it
was stupid to work on the same machine -- but in none of the cases was
I actually working on a live web site but ancillary applications.

So you were not working on the live server application, were you?
But he didn't say, "it is quite stupid to do web development work on a
production or deployment web server." If he had meant to say that, I'm
sure he would.

I did. I explicitely said live server.
That's what you understood him to say, but it's not
what he actually said. It seems to be that you have made some
assumptions that weren't explicity stated in JUE's statement and are
taking me to task because I don't share your assumptions.

No he isn't. He is interpreting my words exactly as I intended them and
exactly as is customary in the relevant industry.
If you are not familiar with those terms then that is your problem, not
mine or Sherm's.

jue
 
J

Justin C

I'm stuck once again. I want to be able to store a hash of events sorted by
Date (or perhaps DateTime) in a text file, and read back into the hash where
I can add new events or delete old ones. The hash is defines as:

my %Event_DB = (
$E_Date => {
Date => $E_Date, #This was previously an array
Time => $E_Time,
Title => $E_Title,
Descr => $E_Descr,
},
);

I am storing the data received from the HTML form as follows:

my $filename = "events.txt";

#*** Can't create file in cgi-bin; must create empty file and chmod 777
#unless (-e $filename) { #check for exist
open (EventFile, '>>', $filename)
or HTMLdie ("File write error: $!");

for my $event (map $Event_DB{$_}, sort keys %Event_DB) {
print (EventFile $event->{Title});
print (EventFile $event->{Date});
print (EventFile $event->{Time});
print (EventFile $event->{Descr});
}
close (EventFile);
# }
Try 'Learning Perl' ch17 seeing as you appear to have access to the Perl
CD Bookshelf.


Is this a legitimate way to access the Perl CD Bookshelf?


Justin.
 
P

Paul E. Schoen

Uri Guttman said:
PES> Maybe I should uninstall the DotCMS host and install an Apache
server?

yes, yes, and YES. no reason to use anything but apache. you can do so
many things on your own and not need your external web host for anything
but production.

The server that will most likely host this is Apache. But as mentioned
elsewhere setting it up is non-trivial and one more thing I don't want to
mess with at this point. I can run the script locally but I had to use goto
statements to spaghetti code around the CGI stuff and simulate the data from
the HTML page.

Maybe there is a way to detect that there is no CGI data and provide the
simulated stuff without having to change the source code for local or remote
(live server?) use.

I have finally achieved most of my goal for this script, and I am now using
a DBI database connection. It seems much easier to me than using hashes and
worrying about data separators and \n newlines in some fields. Here is
roughly what I've done:

use warnings;
use strict;
use DBI;
use Fcntl;
use DateTime;
.... Get CGI data and mail a copy to me
my $E_Date = $in{'Event_Date'}; #Date in yyyymmdd format
my $E_Time = $in{'Event_Time'}; #Time in hh:mm format
my $E_Title = $in{'Event_Title'};
my $E_Descr = $in{'Event_Description'}; #Description may have newlines
#This will replace the $E_Date and $E_Time; using "now" to simulate
my $E_DT = DateTime->now;

my $dbfile = "events.db";
my $db = DBI->connect( # connect to your database, create if
needed
"dbi:SQLite:dbname=$dbfile", # DSN: dbi, driver, database file
"", # no user
"", # no password
{ RaiseError => 1, AutoCommit => 1 } # complain if something goes wrong
) or die $DBI::errstr;

unless (-e $dbfile) { #check for exist
$db->do("CREATE TABLE tEvents (dt DATETIME PRIMARY KEY, tl TEXT, de
TEXT)");
}
$db->do("PRAGMA foreign_keys = OFF");
$db->do("INSERT INTO tEvents (dt,tl,de) VALUES ('$E_DT', '$E_Title',
'$E_Descr')");

my $all = $db->selectall_arrayref("SELECT * FROM tEvents");

# Print the data for debugging purposes
foreach my $row (@$all) {
($E_DT, $E_Title, $E_Descr) = @$row;
my @dt = split('T',$E_DT); #I tried using $E_DT->mdy with no joy
my $d = $dt[0]; #Extract the date portion yyyy-mm-dd
my $t = $dt[1]; #Extract the time portion hh:mm:ss
print "$E_Title\n$d $t\n$E_Descr\n";
}

# Print the data in HTML to the user
[snip]

# Print to the HTML file (write, append, create)
# Yes I know I need to change the HTML format
open (DATA1, '>', "output.htm")
or HTMLdie ("File error: $!");

print DATA1 <<EOF ;
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<title>iframe - Events - Greater Baltimore Group Sierra Club Web
Site</title>
</head>
<body>
<h1>Events</h1><hr>
EOF

foreach my $row (@$all) {
($E_DT, $E_Title, $E_Descr) = @$row;
my @dt = split('T',$E_DT);
my $d = $dt[0];
my $t = $dt[1];
print DATA1 "<p><h3>$E_Title</h3>\n";
print DATA1 "<h4>$d $t</h4>\n";
print DATA1 "$E_Descr<hr>\n";
}

print DATA1 <<EOF ;
</body>
</html>
EOF

close (DATA1);

That's about all there is to it, for now. I need to clean up some things but
it seems to do the job, and it was a learning experience. I can see how
powerful Perl is and how it may be convenient for the language to take care
of the low-level details and use adaptive context so that types need not be
declared, but it's still confusing and frustrating for me. I've spent many
late-late hours on this and most of that was learning how to do relatively
simple things, and also dealing with as yet unknown problems such as the
inability to use the datetime format functions, and also why the interpreter
would sometimes give an error of table tEvents not found.

Many thanks to all who have helped me with this. Hopefully I can now get
busy with some of the other details to make this useful and secure. I will
definitely need to store the database and HTML files someplace other than
the cgi-bin directory. The webmaster for the Sierra Club says their server
does not support CGI so I will need to host that part myself.

Up until now all events were submitted to the webmasters of the various
chapters and local groups and they had to update the content. I think our
Greater Baltimore Group's site has been maintained manually and the events
were often delayed by weeks or months. The other sites that we might want to
emulate appear to use a CMS of some kind, and for a while I used Atomz for
the Maryland site, but I found it difficult to use, and now I think Atomz is
no longer available.

Here are the websites mentioned:

http://maryland.sierraclub.org
http://maryland.sierraclub.org/baltimore I have placed links here to the
live event submission form and the new site under construction
http://maryland.sierraclub.org/Montgomery I used this site as a template
for the one under construction
http://maryland.sierraclub.org/Catoctin I might use this model instead,
as it seems simpler and it fills the entire width of the screen

Paul
 
J

Jürgen Exner

Paul E. Schoen said:
The server that will most likely host this is Apache. But as mentioned
elsewhere setting it up is non-trivial and one more thing I don't want to
mess with at this point. I can run the script locally but I had to use goto
statements to spaghetti code around the CGI stuff and simulate the data from
the HTML page.

???
You did read the section "DEBUGGING" in "perldoc CGI", didn't you?

DEBUGGING
If you are running the script from the command line or in the perl
debugger, you can pass the script a list of keywords or
parameter=value
pairs on the command line or from standard input [...]
Maybe there is a way to detect that there is no CGI data and provide the
simulated stuff without having to change the source code for local or remote
(live server?) use.

Yes, as documented in the documentaton of the tool you are using.
I have finally achieved most of my goal for this script, and I am now using
a DBI database connection. It seems much easier to me than using hashes and

Hashes are not that bad. Just think of them as arrays that use text
instead of numbers as indices.

jue
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,769
Messages
2,569,579
Members
45,053
Latest member
BrodieSola

Latest Threads

Top