Question

sample.cpp

struct InstanceConfig_1
{
   unsigned int   m_Ssn; 
   char           m_StackName [31];
   unsigned int   m_MaxMsgs;
   char           m_LogicalName  [29];
};

struct InstanceConfig_2
{
   unsigned int   m_Ssn; 
   char           m_StackName [31];
   unsigned int   m_MaxMsgs;
   char           m_LogicalName  [29];
};

Compare.pl

use strict;
use warnings;

use Data::Dumper;

my $each;
open FILE, "sample.cpp";

my @inst1 = ();
my @inst2 = ();

my $start = 0;
foreach $each (<FILE>)
{
   chomp $each;
   if($each =~ /^[ \t]*$/) # skip empty lines
   {
      next;
   }
   if($each =~ /{/)
   {
      $start = 1;
   }
   elsif($each =~ /}/)
   {
      $start = 0;
      last;
   }
   elsif($start > 0)
   {
      $each =~ s/^\s+//; # remove leading space
      $each =~ s/\s+$//; # remove trailing space
      $each =~ s/\s+/ /g; # replace multiple space with single space
      push(@inst1, $each);
   }
}

foreach $each (<FILE>)
{
   chomp $each;
   if($each =~ /^[ \t]*$/) # skip empty lines
   {
      next;
   }
   if($each =~ /{/)
   {
      $start = 1;
   }
   elsif($each =~ /}/)
   {
      $start = 0;
      last;
   }
   elsif(1 == $start)
   {
      $each =~ s/^\s+//; # remove leading space
      $each =~ s/\s+$//; # remove trailing space
      $each =~ s/\s+/ /g; # replace multiple space with single space
      push(@inst2, $each);
   }
   print $each;
}

print Dumper(\@inst1);
print Dumper(\@inst2);

output

$VAR1 = [
          'unsigned int m_Ssn;',
          'char m_StackName [31];',
          'unsigned int m_MaxMsgs;',
          'char m_LogicalName [29];'
        ];
$VAR1 = [];

why @inst2 is empty?

Was it helpful?

Solution 3

As @Wumpus Q. Wumbley said the in foreach $each () is not retrieving the lines one by one. It is executed once, at the start of the loop, returning a list of all the lines. The loop then iterates over that list. lasting out of the loop doesn't undo the "read all lines" operation that happened before the loop started.

OTHER TIPS

Try this script:

#!/usr/bin/perl

use strict;
use warnings;

use Data::Dumper;

open my $file, '<', 'sample.cpp'
    or die "Cannot open sample.cpp: $!";

my @insts;

while (<$file>) {
    chomp;
    if (m/\s*{/) {
        next if m/^\s*$/;
        my @inst;
        while (<$file>) {
            last if m/\s*}/;
            s/^\s*|\s*$//g;
            s/\s+/ /g;
            push @inst, $_;
        }
        push @insts, \@inst;
    }
}

print Dumper(\@insts);

To match empty lines, use /^\s*$/. The \s matches spaces and tabs.

Your problem is that you're using for. A for loop will slurp up all of the file into a virtual array, and then assign each element. When you leave the first loop and enter the second, you can't read any more lines because the entire file has been read.

Always use while when reading in files. It's more memory efficient (because it doesn't have to read the entire file into memory), faster (again because it doesn't have to read the entire file into memory), and less error prone (because it doesn't read... You get the idea).

Use for only when you have an actual array since it's already in memory. Just be careful because if you change an element in the array, you change the actual array:

my @array = qw(1 2 3 4 5);

for my $number ( @array ) {
    if ( $number == 3 ) {
       my $number = "bang!";
    }
}

After this loop, the array will be 1, 2, bang!, 4, 5:

Here's your program with pretty much your logic intact, and the replacement of the for with while. I've updated the syntax a bit to make it more modern, but you should see your logic:

#!/usr/bin/env perl
use strict;
use warnings;
use feature qw(say);
use autodie;

use Data::Dumper;

open my $fh, "<", "sample.cpp";

my @inst1;
my $start = 0;
while ( my $each = <$fh> ) {
    chomp $each;
    next if $each =~ /^\s*$/;
    last if $each =~ /\}/;
    if ( $each =~ /{/ ) {
        $start = 1;
    }
    elsif ( $start > 0 )
    {
        $each =~ s/^\s+//; # remove leading space
        $each =~ s/\s+$//; # remove trailing space
        $each =~ s/\s+/ /g; # replace multiple space with single space
        push @inst1, $each;
    }
}

my @inst2;
$start = 0;
while ( my $each = <$fh> ) {
    chomp $each;
    next if $each =~ /^\s*$/;
    last if $each =~ /\}/;
    if ( $each =~ /{/ ) {
        $start = 1;
    }
    elsif ( $start > 0 )
    {
        $each =~ s/^\s+//; # remove leading space
        $each =~ s/\s+$//; # remove trailing space
        $each =~ s/\s+/ /g; # replace multiple space with single space
        push @inst2, $each;
    }
}

say Dumper \@inst1;
say Dumper \@inst2;
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top