Beefy Boxes and Bandwidth Generously Provided by pair Networks
Just another Perl shrine
 
PerlMonks  

Multiple simultaneous filehandles

by ezekiel (Pilgrim)
on Oct 24, 2006 at 04:55 UTC ( [id://580195]=perlquestion: print w/replies, xml ) Need Help??

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

I have a tab-delimited text file of an unspecified number (say x) columns. I want to split this into x-1 separate tab-delimited text files where the first file contains just the first and second columns, the second file contains the first and third columns, etc.

As I don't know the number of columns in advance, the filehandles for the output files have to be variables. So I have the following code:

use strict; my $number_of_files = 0; while (<STDIN>) { chomp; my @line = split; # On the first list, set up the process if ($. == 1) { for (my $i = 1; $i < @line; $i++) { my $fh = "OUTPUT" . $i; my $filename = $line[$i] . ".txt"; open ($fh, ">$filename") or die "Cannot open the output fi +le: $!"; } $number_of_files = @line; } else { for (my $i = 1; $i < @line; $i++) { my $fh = "OUTPUT" . $i; print $fh ("$line[0]\t$line[$i]\n"); } } } for (my $i = 1; $i <= $number_of_files; $i++) { my $fh = "OUTPUT" . $i; close $fh or die "Cannot close the output file: $!"; }

The problem is I get a "Can't use string ("OUTPUT1") as a symbol ref while "strict refs" in use" error. Where have I gone wrong?

Replies are listed 'Best First'.
Re: Multiple simultaneous filehandles
by gaal (Parson) on Oct 24, 2006 at 05:26 UTC
    You're trying to use talk about the filehandle symbolically, that is, construct its name:

    my $fh = "OUTPUT" . $i;

    Don't do that. Since version 5.6, Perl allows you to conveniently put filhandles in lexically scoped variables. In your case you're indexing by a number, so an array of handles is probably the best choice.

    # before the if ($. == 1) my @fhs; ... # open one open $fhs[$i], ">", $filename or die "Cannot open the output file: $!" +; ... # use it print {$fhs[$i]} "$line[0]\t$line[$i]\n";

    Note it's important to control the scope of @fhs, so it's visible to your code that uses it. When it goes out of scope, the files are closed.

    Update: fixed syntax error, thanks eyespoplikeamosquito++!

      # use it print $fhs[$i] "$line[0]\t$line[$i]\n";
      That is a syntax error. Better style is:
      print {$fhs[$i]} "$line[0]\t$line[$i]\n";
      See Perl Best Practices, Chapter 10, "Printing to Filehandles".

      Update: Or, if you prefer OO-style IO:

      use IO::Handle; # ... $fhs[$i]->print("$line[0]\t$line[$i]\n");

Re: Multiple simultaneous filehandles
by grep (Monsignor) on Oct 24, 2006 at 05:41 UTC
    There is little (unless these files are huge) need to make this so complicated. Just create the structure of the file in a AoAoA and process. No need to worry about multiple file handles, just use one over and over again. Whenever I tackle something like this, I almost always create the data structure and then process.

    If you're having a tough time coming up with a solution, it's almost always a good idea to visulize/write down the data structure. Then the program makes itself. Oh and Data::Dumper is your friend.

    use strict; use warnings; my @file = ( '1 2 3 4', '5 6 7 8', '9 10 11 12', '13 14 15 16'); my @data; foreach my $line (@file) { my @fields = split(/\t/,$line); for (my $x = 1 ; $x <= $#fields ; $x++) { push @{$data[$x-1]}, [$fields[0],$fields[$x]]; } } for ( my $x = 0 ; $x <= $#data; $x++) { open(FH,">OUTPUT_$x") or die "$!\n"; foreach my $fields ( @{$data[$x]} ) { print FH "$fields->[0] $fields->[1]\n"; } close FH; }


    grep
    One dead unjugged rabbit fish later
Re: Multiple simultaneous filehandles
by blazar (Canon) on Oct 24, 2006 at 12:15 UTC
    The problem is I get a "Can't use string ("OUTPUT1") as a symbol ref while "strict refs" in use" error. Where have I gone wrong?

    Others already told you what you're doing wrong, and I also concur with grep that you're making things overly complicated. I want to contribute, and here's how I'd do it:

    #!/usr/bin/perl use strict; use warnings; chomp(my $line=<>); my @line=split /\t/, $line; { my @fh=map { my $fname=sprintf 'output%02d.txt', $_; open my $fh, '>', $fname or die "Can't open `$fname': $!\n"; $fh } 1..$#line; sub doline { my $one=shift; print { $fh[$_] } "$one\t$_[$_]\n" for 0..$#_; } } doline @line; chomp, doline split /\t/ while <>; __END__

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://580195]
Approved by BrowserUk
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others chanting in the Monastery: (7)
As of 2024-04-18 07:56 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found