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

  Main Index MAIN
Search Posts SEARCH
Who's Online WHO'S
Log in LOG

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



Nov 2, 2016, 7:49 AM

Post #1 of 3 (4536 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:

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:

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?

Veteran / Moderator

Nov 2, 2016, 11:02 AM

Post #2 of 3 (4532 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.

Veteran / Moderator

Nov 2, 2016, 2:17 PM

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


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):

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:

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:

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

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:

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