http://qs321.pair.com?node_id=69802

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

I have this script which loads a table from __DATA__ thusly:
my @Table; sub LoadTable { local $/; @Table = map {chomp; hex} split ' ', <DATA>; close DATA; }
Now, I close the DATA Filehandle because I don't like "<DATA> line 1." being appended to my errors and exceptions. Which is ok, so long as I only want to call that method once. But I want to call the method multiple times, which yeilds the wonderful message: "readline() on closed filehandle main::DATA at D:\test.pl line 5." Which raises the questions:

    Can you re-open the DATA filehandle? How?

Replies are listed 'Best First'.
(tye)Re: Closing and re-opening the DATA Filehandle
by tye (Sage) on Apr 04, 2001 at 23:14 UTC

    Ugly hack:

    my $pos= tell(DATA); chomp( my $x= <DATA> ); warn "Testing($x)"; { my $fh= do { local(*FH); \*FH }; open $fh, "<&DATA" or die "Can't save DATA: $!\n"; close DATA or warn "Can't close DATA: $!\n"; open DATA, "<&=".fileno($fh) or die "Can't restore DATA: $!\n"; seek DATA, $pos, 0 or die "Can't reposition DATA: $!\n"; } warn "Testing"; chomp( $x= <DATA> ); warn "Testing($x)"; __END__ some data
    produces
    Testing(some data) at redata.pl line 4, <DATA> line 1. Testing at redata.pl line 12. Testing(some data) at redata.pl line 14, <DATA> line 1.
    which is what you asked for but needs some serious encapsulation work at least.

            - tye (but my friends call me "Tye")
      Nope. That doesn't work either. I wrapped your code into a sub, so I could call it multiple times and got:
      Testing(some data) at D:\test.pl line 5, <DATA> line 1. Testing at D:\test.pl line 13. Testing(some data) at D:\test.pl line 5, <DATA> line 1. Can't save DATA: Bad file descriptor
      Here is test.pl:
      sub Tye { my $pos= tell(DATA); chomp( my $x= <DATA> ); warn "Testing($x)"; { my $fh= do { local(*FH); \*FH }; open $fh, "<&DATA" or die "Can't save DATA: $!\n"; close DATA or warn "Can't close DATA: $!\n"; open DATA, "<&=".fileno($fh) or die "Can't restore DATA: $!\n"; seek DATA, $pos, 0 or die "Can't reposition DATA: $!\n"; } warn "Testing"; } Tye(); Tye(); __END__ some data

        My mistake. I know this doesn't matter since you found a better solution but this bugged me and I found my mistake.

        I was thinking that the "=" in "<&=" was required when using a numeric file descriptor. But the "=" requests an fdopen() instead of a dup(), that is, the new file handle ends up sharing the same file descriptor with the old file handle. So when $fh is closed (triggered when it goes out of scope and gets destroyed), the file descriptor that DATA is using also gets closed out from under it.

        So just drop the "=" and my code works.

                - tye (but my friends call me "Tye")
Re: Closing and re-opening the DATA Filehandle
by Adam (Vicar) on Apr 05, 2001 at 02:22 UTC
    HA! I figured it out!

    This works, and no filehandles in the error messages:

    my @Table; sub LoadTable { local( $/, $. ); my $position = tell( DATA ); @Table = map {chomp; hex} split ' ', <DATA>; seek DATA, $position, 0; # Reset the filehandle, rather then closi +ng it. # Never close DATA. }
    But if I take $. out of the local line, then the filehandle shows up in the error messages. Thanks all!
(Ovid - seek)Re: Closing and re-opening the DATA Filehandle
by Ovid (Cardinal) on Apr 04, 2001 at 23:11 UTC
    Rather than opening and closing the file, how about opening it once and using seek DATA, 0, 0 to reposition yourself at the start of __DATA__? Anyone see a problem with this approach?

    Cheers,
    Ovid

    Update: Oops. I keep forgetting that __DATA__ doesn't behave like a normal filehandle. See tye's answer below.

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

      0, 0 is not the position of __DATA__, its the begining of the file.

      Also, seek still leaves the filehandle attached to error messages, which I don't want.

Re (tilly) 1: Closing and re-opening the DATA Filehandle
by tilly (Archbishop) on Apr 05, 2001 at 07:23 UTC
    My solution would be to side-step the question with some memoization.
    my @Table; { my @_Table; sub LoadTable { unless (@_Table) { local $/; @_Table = map {chomp; hex} split ' ', <DATA>; close DATA; } @Table = @_Table; } }
    Issues with this? Well first of all you are assuming that DATA doesn't have too much in it, and you assume that it has something.

    But still memoization is a good solution to many problems where you wouldn't have thought it applied at first....

Re: Closing and re-opening the DATA Filehandle
by AgentM (Curate) on Apr 05, 2001 at 03:18 UTC
    This is truly a perplexing problem. Here's a possible solution involving pipes: (tested code)
    #!/usr/bin/perl use strict; use IO::Handle; pipe(READ,WRITE) or die; READ->autoflush(1); #just to be safe WRITE->autoflush(1); my $data=<DATA>; print $data; print WRITE $data; *DATA=*READ; my $data2=<DATA>; print $data2; __DATA__ TESTDATA
    It kind of shocked me that *DATA was not read-only. After reading from the pipe, just write the same thing back and you'll have an infinite supply of DATA. This can be easily adapted for lines of DATA but be beware of autoflushing. I came across some pitfalls involving non-flushing data in the handles when I was doing something similar.
    AgentM Systems nor Nasca Enterprises nor Bone::Easy nor Macperl is responsible for the comments made by AgentM. Remember, you can build any logical system with NOR.
a flash of inspiration...
by cLive ;-) (Prior) on Apr 05, 2001 at 00:52 UTC
    I assume you "exit" your program when done.

    why not use this instead:

    # replace exit(0); with: exit(close(DATA)); # or (at beginning of script) sub exit_script { close(DATA); exit(0); } # then replace your exit calls with exit_script;
    timtowtdi though...

    cLive ;-)

      Actually, no. I'm writing a module, and short of overloading CORE::exit I don't have control over the exit. That's also why I don't want <DATA> to show up in error messages. Why would the user of my module care if I'm reading the module's DATA? They wouldn't.

      On a side note, I'm not sure I understand your answer anyway. The problem isn't with exit, its with warn and die. Specifically, if you have an open filehandle then Perl mentions that when formating your error messages. That's one of the many good reasons to close filehandles when you are done with them, and to use IO::File which observes scope. (normal filehandles are global - although later versions of Perl allow you to scope them via my) My problem is that DATA is not a normal filehandle (as Ovid remembered above). I can't close it, because you can't open it. But once I've read from it, it shows up in error messages. So the question is, how do you keep it open for re-reading, without it showing up in error messages.

        Am I missing something here?

        Is there any reason why you can't read in the data when the module is loaded, and assign it when you need to?!? ie:

        package mod_name; { local $/; @mod_name::TableTemplate = map {chomp; hex} split ' ', <DATA>; close DATA; } my @Table = @mod_name::Table_Template;

        Or am I missing something completely obvious ?!?

        cLive ;-)