Beefy Boxes and Bandwidth Generously Provided by pair Networks
Think about Loose Coupling

Question about Open and Close

by Ebany (Sexton)
on Jun 08, 2002 at 19:22 UTC ( #172821=perlquestion: print w/replies, xml ) Need Help??
Ebany has asked for the wisdom of the Perl Monks concerning the following question:

Hello, I'm fairly new to perl, and am trying to learn it by developing an application for my own use. I am basically trying to write out html pages of my music collection. I read in from an Access database, and then write out the html, each artist to their own page, and creating sections for each album within the artist's page, and then a main index.html . My question is, the code works. True, it may be baby perl, but fact is it does what I want it to do, except I am having a problem with the Open and Close commands. The html is written, but I get "print() on closed filehandle ARTISTOUT at C:\PROGRA~1\APACHE~1\APACHE\CGI-BIN\ex line 96.", which is this line: "print "<tr><td>$row[1] - $row[2] - $row[0]</td></tr>";". I am dealing with roughly ~14000 entries here, and I think that the problem may be I am trying to write out again before the previous process is finished. It works on smaller groups of entries (a few hundred) with no problems.
#! d:/perl/bin/perl -w use DBI; use File::Basename; use strict; open MASTEROUT, ">index.html"; select MASTEROUT; my $DSN = 'driver=Microsoft Access Driver (*.mdb);dbq=Media.mdb'; my $dbh = DBI->connect("dbi:ODBC:$DSN", '','') or die "$DBI::errstr\n" +; my $artistHolder; my $albumHolder; my $query = $dbh->prepare("SELECT title, artist, album FROM tblMedia O +RDER BY artist, album"); $query->execute(); my @row = $query->fetchrow_array; $artistHolder = $row[1]; $albumHolder = $row[2]; print "<!DOCTYPE html PUBLIC '-//W3C//DTD XHTML 1.0 Transitional//EN' +''>"; print MASTEROUT "<html><head><title>Overall</title></head><body><table + summary='Overall' width='640' align='center' border='1'><tr><td><tab +le summary='Artist Listing' width='100%'>"; print MASTEROUT "<tr><td><a href='$row[1].html'>$row[1]</a></td></tr>" +; open ARTISTOUT, ">".$row[1].".html"; select ARTISTOUT; #Overall table print "<!DOCTYPE html PUBLIC '-//W3C//DTD XHTML 1.0 Transitional//EN' +''>"; print "<html lang='en-US'><head><title>$row[1]</title>"; print "<link rel='stylesheet' type='text/css' href='style.css'></link> +</head><body>"; print "<table summary='Overall' border='1' width='640' align='center'> +<tr><td>"; #per artist print "<table summary='Artist' width='100%' border='0'>"; #per album print "<tr><td colspan='2'><b>$row[1]</b></td></tr>"; print "<tr>"; print "<td width='5%'>&nbsp;</td>"; print "<td><table summary='Album' width='100%' align='center' border=' +0'>"; print "<tr><td class='tableTitle'>$row[1]</td></tr>"; while ( @row ) { #Control break on Album # if (@row[2] ne $albumHolder && @row[1] ne $artistHolder) if ($row[2] ne $albumHolder && $row[1] eq $artistHolder) { print "</table></td></tr>"; $albumHolder = $row[2]; print "<tr><td>&nbsp;</td><td><table summary='Album' width='10 +0%'><tr><td><br /><br /><i>$row[1] - $row[2]</i></td></tr>"; print "<tr><td><hr /></td></tr>"; } #Control break on Artist if ($row[1] ne $artistHolder) { #End previous artist's listings (End album, then artist print " +</table></td></tr></table></td></tr></table></body></html>"; close ARTISTOUT; open ARTISTOUT, ">".$row[1].".html"; select ARTISTOUT; print MASTEROUT "<tr><td><a href='$row[1].html'>$row[1]</a></t +d></tr>"; #Begin new print "<!DOCTYPE html PUBLIC '-//W3C//DTD XHTML 1.0 Transition +al//EN' ''>"; + print "<html lang='en-US'><head><title>$row[1]</title>"; print "<link rel='stylesheet' type='text/css' href='style.css' +></link>"; print "</head><body><table summary='' width='640' align='cente +r'><tr><td><table summary='Artist' width='100%' border='0'>"; print "<tr><td colspan='2'><hr /></td></tr>"; print "<tr><td colspan='2' class='tableTitle'><b>$row[1]</b></ +td></tr>"; print "<tr><td colspan='2'><hr /></td></tr>"; $artistHolder = $row[1]; $albumHolder = $row[2]; #start out next album print "<tr><td width='5%'>&nbsp;</td><td><table summary='Album +' width='100%' border='0'>"; print "<tr><td colspan='2'><a href='index.html'>Back to home</ +a><br /><br /></td></tr>"; print "<tr><td colspan='2'><i>$row[1] - $row[2]</i></td></tr>" +; print "<tr><td><hr /></td></tr>"; } #Print out each entry print "<tr><td>$row[1] - $row[2] - $row[0]</td></tr>"; #Get the next entry. @row = $query->fetchrow_array; } print "</td></tr></table></html>"; print MASTEROUT "</table></td></tr></table></body></html>"; $dbh->disconnect();
I ask for a little guidance. Can I throttle this to wait for the previous process? Please don't fry me too badly..

Replies are listed 'Best First'.
Re: Question about Open and Close
by DamnDirtyApe (Curate) on Jun 08, 2002 at 19:46 UTC

    Two pieces of advice:

    • Check that your files are actually being opened. open ARTISTOUT, ">".$row[1].".html" or die "Cannot open file: $!\n" ; might give you a clue as to why you program is trying to write to a closed filehandle.
    • You really ought to take a look at the CGI module for this. You may find that you can clean up your code significantly. Or, if you get really ambitious, go check out the Template Toolkit. It is absolutely my favourite tool for database-to-CGI applications.

    Good luck! :->

    UPDATE: Just reread the problem. It sounds to me as though buffering could be an issue. Try adding the line $|++ ; near the beginning of your code and see if it helps any.

    D a m n D i r t y A p e
    Home Node | Email

      It's not clear that this program is designed to be used as CGI script. HTML has become the defacto documentation form on Windows machines. However,'s HTML generation reoutines still might be useful.


      Light a man a fire, he's warm for a day. Catch a man on fire, and he's warm for the rest of his life. - Terry Pratchet

        True, so a much better thing would be to go get HTML::Template, b/c its damn sexy.
Re: Question about Open and Close
by graff (Chancellor) on Jun 09, 2002 at 03:06 UTC
    A few comments, which I hope will be helpful:
    • When you have multiple output files open at one time, and use "select FILEHANDLE" on one, then another, you're inviting confusion about where a given 'print $string' is really going.
    • When your print statements are using the same string over and over (e.g. '<DOCTYPE>...'), it makes sense to assign that string to a variable, and use the variable in all those print statements.
    • Try to write a brief, simple pseudo-code version of your app, and see if it leads you to compartmentalize things a little better (e.g. to put some portions of code into subroutines).

    I don't know why you got the error that you reported (earlier comments about an open failure that you didn't check for are probably on the mark), but I would like to offer this pseudo-code summary as an example that arranges things a little differently and may suggest some ways to write the code more economically:
    connect_to_db or die prepare_query or die open_master_page or die print_master_page_header $artist_page_open = ""; while ( query_returns_a_row ) { if ( $artist_page_open ne this_artist ) { if ( $artist_page_open ne "" ) { print_artist_page_trailer close_artist_page or die } open_artist_page or die print_artist_page_header $artist_page_open = this_artist } print_artist_page_row print_master_page_row } close_db print_artist_page_trailer print_master_page_trailer
    I'm not saying everything should be a subroutine; but as you start to put details into the stubs and you find things that they have in common, it'll be easier to factor those common things into subs, so you only have to code each one once.
      I had actually wanted to write things in subprocedures, as it's the way I usually do things when programming, because it makes for more readable code, however I was then having problems with printing. I found that if I created an "artistBreak" sub and "albumBreak" sub, and then tried to print from within those subprocedures, I got errors trying to print. I was a little confused if I should open the artist page again within the subprocedure, and then seek to the end and print, or if I should somehow be able to declare an open on the global level, and then print normally. I was also debating if it would be more "proper" to do all "print ALBUMMASTER 'blah blah'" or the way I did, doing a select, and then switching when needed. I agree with you though, it should be cleaned up, and broken into smaller pieces.
Re: Question about Open and Close
by TheHobbit (Pilgrim) on Jun 08, 2002 at 19:37 UTC

    While I cannot realy tell where the problem in your code is, I can give you a good advice: <emph>always test return value of system calls</emph>. That means writing something like:

    open(FOO,">$file") || die "Couldn't open $file: $!";
    This should allow you to find where the error is... May be you'll even be able to found the solution! :)))


    Update: addent parenthesis, thank you gav^. This show me once again a thin: <emph>never, ever post code without trying it!</emph>.

    TheHobbit goes to the monastery kitchens... will peeling 100 Kg potato's be enough to make ammend?

    Leo TheHobbit
      I think you mean: open FOO,">$file" or die "Couldn't open $file: $!";
      or open(FOO,">$file") || die "Couldn't open $file: $!";


Re: Question about Open and Close
by Util (Priest) on Jun 09, 2002 at 00:29 UTC
    I am not clear on what the "previous process" would be, but is seems likely that you have an artist with characters in the name that are invalid in a Windows filename. As others have said, check your open statements. In fact, check your close statements, too; they can fail in some circumstances, like "filesystem full". Please let us know what the problem was!
Re: Question about Open and Close
by Ebany (Sexton) on Jun 09, 2002 at 20:19 UTC
    OK, I think I have figured it out now. I thank everyone who contributed! Indeed, I believe it was a filename problem. I should have put on the dies on the opens and closes, and now I know better. The problem appears to be in filenames with odd characters, such as apostraphes and quotes (i.e. "Weird Al" yankovic, which tried to create a file called "Weird Al" yankovic.html which died.) Also was the problem of files that had no artist associated with them, which then created a file ".html", which is not quite what I want. So, I now have to figure out how to scrub out the invalid characters, and some way of catagorizing the odd files that have no artist. Again, thank you for your help!

Log In?

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://172821]
Approved by virtualsue
NodeReaper puts the hammer down

How do I use this? | Other CB clients
Other Users?
Others romping around the Monastery: (7)
As of 2017-07-29 12:59 GMT
Find Nodes?
    Voting Booth?
    I came, I saw, I ...

    Results (436 votes). Check out past polls.