CGI/Perl Guide | Learning Center | Forums | Advertise | Login
Site Search: in

  Main Index MAIN
INDEX
Search Posts SEARCH
POSTS
Who's Online WHO'S
ONLINE
Log in LOG
IN

Home: Perl Programming Help: Beginner:
How to use subroutine to write to different files

 



regex2012
User

Nov 2, 2016, 7:49 AM

Post #1 of 3 (3628 views)
How to use subroutine to write to different files Can't Post

I am just beginning to learn about subroutines. I made one like this:


Code
sub fnFileOpener { 
my( $file_name ) = shift ;
open( my $fh, ">$file_name" ) ||
die "can't create $file_name $!" ;
print $fh @_ ;


I am trying to find out how to use it to write to a file - I formally was using this kind of statement to open files:


Code
open (my $file, '>', '/tmp/printout.txt') or die "Could not open file: $!"; 
my $boutput = qx(/usr/bin/printjobs -all);
die "$!" if $?;
print $file $boutput;


I have this kind of a statement with scores of different commands each writing to different files. How would I use the above subroutine to create a file - is it possible?


Laurent_R
Veteran / Moderator

Nov 2, 2016, 11:02 AM

Post #2 of 3 (3624 views)
Re: [regex2012] How to use subroutine to write to different files [In reply to] Can't Post

Your subroutine is correct if you want to call it only once. If you want to call a subroutine repeatedly to write to a file, then you have to change a few things. Typically, in that case , you don't want the opening of the file to be in the same sub as the writing to the file handler.

Please confirm what you want exactly.


Laurent_R
Veteran / Moderator

Nov 2, 2016, 2:17 PM

Post #3 of 3 (3619 views)
Re: [regex2012] How to use subroutine to write to different files [In reply to] Can't Post

Hi,

I had to make my earlier post shorter than I would have wanted because I was posting from the train commuting back home and my train was arriving in my city.

Suppose (as a somewhat silly example) that you have an array of integer numbers and want to store them into two files, one with odd numbers and one with even numbers. If you want to do that in a subroutine (the same subroutine), you could do something like this (untested):


Code
open my $ODD, ">", "odd_file.txt" or die "Cannot open odd_file.txt $!"; 
open my $EVEN, ">", "even_file.txt" or die "Cannot open even_file.txt $!";
for my $num (<2 4 5 2 6 7>) {
if ($num % 2) {
# odd number
print_to_file($ODD, $num);
} else {
print_to_file($EVEN, $num);
}
}

sub print_to_file {
my ($fh, $value) = @_;
print $fh, $value, "\n";
}


In plain English words, you open the file before starting and you pass the filehandle as an argument to your sub.

Now, suppose you have 26 files, one per letter of the alphabet, and want to store words in accordance to their initial letter. You don't want to open manually 26 files, nor to have 26 different variables. You can store your file handles in a hash:


Code
my %file_handles; 
for my $letter ('a'..'z') {
open my $FH, ">", "file_$letter.txt" or die "Cannot open file$letter.txt $!";
$file_handles{$letter} = $FH;
}

# ...
# suppose you have a $word stating with letter $start
#
print_to_file ($file_handles{$start}, $word);


And the print_to_file sub is the same as before.

Yet another way (beware, this is a more advanced technique) would be to create dynamically 26 different anonymous subroutines and to store them in a dispatch table, i.e. a hash of code references:


Code
my %dispatch_table; 
for my $letter ('a'..'z') {
$dispatch_table{$letter} = create_sub($letter);
}

sub create_sub {
my $let = shift;

open my $FH, ">", "file_$letter.txt" or die "Cannot open file$letter.txt $!";
return sub {
my $value = shift;
print $FH, $value, "\n";
}
# ...
# suppose you have a $word stating with letter $start
#
$dispatch_table{$start}->($word);


But this latest example is using some fairly advanced concepts, I would not recommend it if you don't know about things such as anonymous subroutines, code references, closures, dispatch tables, etc. (But I am ready to explain if you wish.)

The latest example above has not been tested, but this is a complete and very similar example that I have tested and used successfully a couple of years ago:

Code
use strict; 
use warnings;

my %dispatch;
$dispatch{$_} = create_sub($_) for ('a'..'z');
while (<>) {
$dispatch{$1}->($_) if (/^([a-z])/) ;
}

sub create_sub {
my $letter = shift;
my $file_name = "letter_$letter.txt";
open my $FH, ">", $file_name or die "Cannot open $file_name $!";
return sub { my $line = shift; print $FH $line; }
}



I hope this helps.


(This post was edited by Laurent_R on Nov 2, 2016, 2:23 PM)

 
 


Search for (options) Powered by Gossamer Forum v.1.2.0

Web Applications & Managed Hosting Powered by Gossamer Threads
Visit our Mailing List Archives