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.
Perl, read file with consecutive for loop not working
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?
La solution 3
Autres conseils
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;