http://www.perlmonks.org?node_id=636998

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

I want to create a subroutine that prints a "standard" header that I need for text files. I would like to pass the FILEHANDLE in to this subroutine.
This works.
open (RM3DFile, ">", $fullFileName ) || die("Could NOT open " . $ful +lFileName . "\n"); rm3dHeader(RM3DFile); close RM3DFile; # RM3D File header sub rm3dHeader($) { print RM3DFile "START_OF_FILE\n"; print RM3DFile "\n"; print RM3DFile "DATEFORMAT=YYYYMMDD\n"; print RM3DFile "DECIMALSEPARATOR=. \n"; }
I would like to pass in the file handle (RM3DFile).
I tried the following:
open (RM3DFile, ">", $fullFileName ) || die("Could NOT open " . $ful +lFileName . "\n"); rm3dHeader(RM3DFile); close RM3DFile; # RM3D File header sub rm3dHeader($) { print $_[0] "START_OF_FILE\n"; print $_[0] "\n"; print $_[0] "DATEFORMAT=YYYYMMDD\n"; print $_[0] "DECIMALSEPARATOR=. \n"; }
but got compile errors
Thanks in Advance.
kd

Replies are listed 'Best First'.
Re: passing a file handle to a subroutine
by rhesa (Vicar) on Sep 04, 2007 at 18:50 UTC
    I recommend using lexical filehandles:
    open my $rm3d_fh, '>', $fullFileName or die "...$!"; rm3dHeader( $rm3d_fh ); sub rm3dHeader { my $fh = shift; print $fh "START..."; }
    I think that's easier to understand than trying to pass a glob.
      that worked first try.
      many thanks
      kd
Re: passing a file handle to a subroutine
by Fletch (Bishop) on Sep 04, 2007 at 19:09 UTC

    Seconded on using a lexical to hold your filehandle, although foo( \*HANDLE ) would do in a pinch (just remember that the filehandle "slot" for a print statement needs to be a simple scalar variable; you would need to use a block (print { $var[$index] } "Blah\n") or store something more complicated in a temp scalar otherwise).

    Having said that, something else in your code jumps out as meriting comment: you really don't want to be using prototypes; they're meant for making subs perform like builtins, not for declaring number-of-arguments. If you really want behavior more like what's normally referred to as prototypes in other languages look at something like Params::Validate.

Re: passing a file handle to a subroutine
by graff (Chancellor) on Sep 05, 2007 at 04:36 UTC
    Personally, if I'm creating a subroutine whose purpose is to create a specific sort of file header, I would rather just have the subroutine return a single string value that is the file header, so that the caller can use the return value and does not need to pass around a file handle -- e.g.:
    open( OUTPUT, ">", "some.file" ) or die "$!"; print OUTPUT rm3dHeader(); print OUTPUT rm3dData(); close OUTPUT; sub rm3dHeader { return "START_OF_FILE\n\nDATEFORMAT=YYYYMMDD\nDECIMALSEPARATOR=.\n +"; } sub rm3dData { return "this is the data\n"; }

    This would provide some flexibility that might be handy, and generally helps to keep the "make_header" logic independent from the "create/write/close file" logic.

      Thanks.
Re: passing a file handle to a subroutine
by blazar (Canon) on Sep 06, 2007 at 12:02 UTC
    I want to create a subroutine that prints a "standard" header that I need for text files. I would like to pass the FILEHANDLE in to this subroutine.

    Not really an answer to your question, but it sprang to mind that you can write custom layers, and that PerlIO::via eases the task when implementing them in Perl, as opposed to C. Since I had never fiddled with this kinda things (experts, please point out any shortcoming/error/whatever), I quickly checked the docs and concocted the following example:

    # -*- Perl -*- use strict; use warnings; package PerlIO::via::MyHeader; our $VERSION = '0.01'; use Carp (); sub PUSHED { my ($class,$mode)=@_; Carp::croak __PACKAGE__, " can be used only for writing to file." unless $mode =~ /w/; bless \(my $x), $class; } my %done; sub WRITE { my ($obj,$buf,$fh)=@_; print $fh ($done{$obj}++ ? '' : <<' HEADER'), $buf; START_OF_FILE DATEFORMAT=YYYYMMDD DECIMALSEPARATOR=. HEADER } 1; __END__

    Admittedly, I used somewhat of an stunt to just print a header, but there's no hook that I can see to do that in a clean way. Be aware that it will slow down your prints, albeit probably in an unnoticeable manner under most common circumstances, but in case performance does matter instead, well... it will matter!

    A test script:

    #!/usr/bin/perl use strict; use warnings; use lib '.'; use PerlIO::via::MyHeader; open my $fh1, '>:via(MyHeader)', "test1" or die "Can't open 'test1': $!\n"; open my $fh2, '>:via(MyHeader)', "test2" or die "Can't open 'test2': $!\n"; print $fh1 "foo\n"; print $fh1 "bar\n"; print $fh2 "foo\n"; print $fh2 "bar\n"; __END__

    And the proof of the pudding, which is in the eating:

    C:\temp>perl testmh.pl C:\temp>diff test1 test2 C:\temp>cat test1 START_OF_FILE DATEFORMAT=YYYYMMDD DECIMALSEPARATOR=. foo bar

    All in all, it was kinda funny. And instructive, it will be even more with some feedback.

    Update: I suppose I should cleanup the %done hash on CLOSE(), but let's fake it had been left as an exercise to the reader.

Re: passing a file handle to a subroutine
by Comyn (Initiate) on Sep 05, 2007 at 17:50 UTC
    Here are two little subs - one gets the name from keyboard input and the other opens the txt and prints to screen. Very basic - grow from there.
    sub getname{ $filename = $_[0]; print "Enter the name of the $filename: "; $x = <stdin>; chomp($x); return $x; } sub printscreen{ $input_file_name = $_[0]; open(infile, "<$input_file_name"); while(<infile>) { $line = $_; print "$line"; } }