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

Hi,This is just another perl newbie
I wrote a piece of code that merges two files.
It takes the first line of the first file ,prints it
It then takes the first line of the second file, prints
it.Then it takes the second line of the files and so on.
The out put is in another file.I would really appreciate
if someone takes a look at it and tells me how to write
the code in a more efficient and compact way.
Thanks
ANKUR
 #!/usr/bin/perl -w
 use strict ;
 my($f1,$f2)=@ARGV;
 open(FILE1,$f1)or die "$!";
 open(FILE2,$f2)or die "$!";
 open(RESULT,">result") or die "$!";

 $line1=<FILE1>;
 $line2=<FILE2>;
while(defined($line1)||defined($line2)){
 if(defined ($line1)){
 print RESULT $line1 ;
 $line1= <FILE1>;
}
 if (defined ($line2)){
 print RESULT $line2 ;
 $line2=<FILE2>;
 }
 }

Replies are listed 'Best First'.
Re: Merging files
by takshaka (Friar) on Aug 27, 2000 at 09:53 UTC
    while (my @lines = grep defined, (scalar <FILE1>, scalar <FILE2>)) { print RESULT @lines; }
      Brilliant!

      But dangerous as a pattern. Once you hit undef on ARGV (also known as the empty file handle or the diamond operator), you should stop reading from it, or you will end up reading from STDIN instead.

      So your post works fine for all filehandles that stay at EOF once you hit EOF. The magic ARGV filehandle does not.

      -- Randal L. Schwartz, Perl hacker

RE (tilly) 1: Merging files
by tilly (Archbishop) on Aug 27, 2000 at 19:21 UTC
    OK, this isn't shorter, but it will merge as many files as you want, and lets you choose the name of the output file from the command-line. :-)
    #! /usr/bin/perl use strict; use Getopt::Std; use Symbol; use vars qw(@handles $opt_f); getopts('f:'); if ($opt_f) { open (STDOUT, ">$opt_f") or die "Cannot write to $opt_f: $!"; } unless (@ARGV) { die "Usage: mergefile [-o<output_file>] file1 file2"; } foreach my $file (@ARGV) { my $fh = gensym; # Letting someone pipe from a cmd is useful enough to allow # having someone try to wipe out a file with ">". open ($fh, $file) or die "Cannot read from $file: $!"; push @handles, $fh; } while (@handles) { my $fh = shift @handles; if (defined( my $line = <$fh> )) { print $line; push @handles, $fh; } # else $fh goes out of scope here and autocloses }
      Oooh.. I like that. Here's another tack at that last bit of code, that'll probably be more memory efficient in the long run:
      while (@handles) { @handles = grep { if (defined(my $line = readline($_))) { print $line; 1; } else { 0 } } @handles; }
      The advantage here is that we're rewriting @handles in one fell swoop in the fairly strongly optimized grep operation, not constantly shifting and pushing on an array that will just keep wandering through higher and higher memory. {grin}

      -- Randal L. Schwartz, Perl hacker

      Um, I tried both code samples, and tilly's worked fine, but merlyn's produces no output.

      It seems to be always hitting the 'else' clause. Changing the readline ($_) to <$_> fixes it.

      Update: jlp reports that merlyns code works for him. I'm running 5_005.03, whereas he's running 5.6.0. I don't know if that would make any difference or not.

      --Chris

      e-mail jcwren
RE: Merging files
by chromatic (Archbishop) on Aug 27, 2000 at 08:34 UTC
    That's pretty compact as is. Here's one possibility, though (untested):
    while ($line1 = <FILE1> && $line2 = <FILE2>) { print RESULT $line1, $line2; }
    The while loop does the equivalent of your defined check.

    Update: merlyn points out that && is too high on the precedence scale to work. Use and instead. Also, this code is likely to fail if you hit a line of '0', as the loop actually does a truth-test. Thanks!

      I think it will also fail if one file happens to be longer than the other file. In that case the tail of the longer file won't be printed. This cause of the &&.

      Have Fun

Re: Merging files
by tye (Sage) on Aug 27, 2000 at 09:33 UTC
    #!/usr/bin/perl -w use strict; die "Usage: $0 file1 file2\n" unless 2 == @ARGV; my($f1,$f2)=@ARGV; open(FILE1,"< $f1\0") or die "$!"; open(FILE2,"< $f2\0") or die "$!"; open(RESULT,">result") or die "$!"; while( defined( $line1= <FILE1> ) and defined( $line2= <FILE2> ) ) { print RESULT $line1, $line2; } if( defined( $line1 ) ) { print RESULT $line1, <FILE1>; } elsif( defined( $line2 ) ) { print RESULT $line2, <FILE2>; }
            - tye (but my friends call me "Tye")