Unable to Write into file within WHILE Loop in Perl

Viewed 260

I'm trying to split a file into multiple files based on lines START and END from the input file. The below code does it fine, but, it is not writing into the output file inside the loop.

use strict;
use warnings;

open my $fh_i, "<", "input.txt" or die $!;

my $in_block = 0;
my $counter = 0;
my $filename = "output_1.txt";

while( <$fh_i> ) {
   $in_block = 0 if /^END/;

   open (my $fh_o, ">", $filename) or die $!;
   print $fh_o $_ if $in_block; #Issue line

   if /^START/
   {
        close $fh_o;
        $in_block = 1 ;
        $counter = $counter + 1;
        $filename = "output_".$counter.'.txt';
    }
}

close $fh_i;

I think the $_ is losing the data when the file is opened. #Issue line is what am worried about. It isn't writing into the file. The files are all empty. What am I missing here?

Sample data in the input file:

START1
line1
line2
line3
END1
START2
line1
line2
line3
END2
START3
line1
END3

Expected result.

output_1.txt should have the below.

START1
line1
line2
line3
END1

output_2.txt should have the below.

START2
line1
line2
line3
END2

output_3.txt should have the below.

START3
line1
END3
2 Answers

You had a few things happening in the wrong order. This is a version of your code that does what you want:

use strict;
use warnings;

open my $fh_i, "<", "input.txt" or die $!;

my $in_block = 0;
my $counter = 0;

my $fh_o;

while( <$fh_i> ) {
    if (/^START/)
    {
        $counter = $counter + 1;
        my $filename = "output_".$counter.'.txt';
        open ($fh_o, ">", $filename) or die $!;
        $in_block = 1 ;
    }

    print $fh_o $_ if $in_block; #Issue line

    $in_block = 0 if /^END/;
}

close $fh_i;

However, there are a few more "Perlish" things that you can do to simplify your code a bit:

use strict;
use warnings;

open my $fh_i, '<', 'input.txt' or die $!;

my $counter = 0;

my $fh_o;

while( <$fh_i> ) {
    # Use the "flip-flop" operator to keep track of whether
    # we should be printing. Removes need for $in_block.
    if (/^START/ .. /^END/) {
        if (/^START/) {
            # Increment the counter
            $counter++;
            # Variables are expanded in double-quoted strings
            my $filename = "output_$counter.txt";
            open $fh_o, '>', $filename or die $!;
        }

        print $fh_o $_;
    }
}

close $fh_i;

Perl code which does not use flip-flop operator offered by Dave Cross

Note: the code does not utilize input file name for flexibility, it is sufficient to give filename as an argument on command line.

Note: run as script.pl input.txt

use strict;
use warnings;

my $count = 0;

while( <> ) {
    if( /^START/ ) {
        $count++;
        my $fname = "output_${count}.txt";
        open my $fh, '>', $fname
            or die "Can't open $fname";
        print $fh $_;
        while( <> ) {
            print $fh $_;
            last if /^END/;
        }
        close $fh;
    }
}
Related