tags:

views:

127

answers:

4

I am storing information captured by regex into an array. But for some reason the first value is getting stored at 4 element of array. Any suggestion on whats going wrong and how to store the first value in the first element of array.

The following is the script:

#!/usr/bin/perl

use strict;
my @value;
my $find= qr/^\s+([0-9]+)\s+([A-Z])/;

open (FILE, "</usr/test")|| die "cant open file";
my @body=<FILE>;

foreach my $line (@body){
    chomp $line;
    push @value, join('', $line =~ /$find/);
}
print "$value[0]\n"; #does not print anything
print "$value[4]\n"; #prints first value i.e 1389E
exit;

DATA

   1389 E not
   188  S yes
   24   D yes
   456  K not
   2    Q yes
+1  A: 

I think you should be writing

print "$value[0]\n";
print "$value[4]\n";

to access elements of an array.

David Zaslavsky
@David: I wrote @value[0] by mistake. I am using $value[0] to print the first value of the array. And, no value is getting printed for first element i.e. $value[0]
shubster
+4  A: 

Your second line has more than one space between the number group and the letter, so you probably want \s+ both times rather than \s the second time.

You won't necessarily know how many items you have in the @value array at the end, so you might want to put the printing into a for loop rather than assume you have a fifth item. (Maybe you know you want the first and fifth, however?) Follow-up: based on your edit, you have more than two entries after all. The version that I give below, using split and \s+ captures the number and letter for all the lines. I'll adjust the print part of the script to show you what I mean.

A few other things:

  • You should always enable warnings.
  • There's no reason to read the whole file into an array and then process through it line by line. Skip the @body array and just do what you need to in the while loop.
  • Use the more modern form of open with lexical filehandles and three arguments.
  • split seems more straightforward here to me, rather than a regular expression with captures. Since you want to capture two specific parts of the line, you can use split with an array slice to grab those two items and feed them to join.
  • @value is not an especially helpful variable name, but I think you should at least make it plural. It's a good habit to get into, I think, insofar as the array stores your plural records. (That's not a hard and fast rule, but it bugged me here. This point is pretty minor.)

Here's how all this might look:

    #!/usr/bin/env perl
    use warnings;
    use strict;

    my @values;

    # open my $filehandle, '<', '/usr/test'
    #     or die "Can't open /usr/test: $!";

    while (my $line = <DATA>) {
        chomp $line;
        push @values, join('', (split /\s+/, $line)[1..2]);
    }

   for my $record (@values) {
      print $record, "\n";
    }

    __DATA__
       1389 E not
       188  S yes
       24   D yes
       456  K not
       2    Q yes
Telemachus
Shouldn't "... form of open with lexical filehandles and three arguments" be used in your code example? And a scope to go out of? Or am I misunderstanding something?
Peter Mortensen
@Peter: I can't actually use the `open` statement, since I don't have the OP's /usr/test file on my drive (and I'm not going to create it for this example). So I wrote the example to show him what I meant, but I use the built-in `__DATA__` to get the files. As far as "a scope to go out of", in a script this short, the problem isn't really scope per se. I wasn't stressing 'lexical' in lexical filehandles; that's just their name. See here for more on bareword filehandles: http://www.perlfoundation.org/perl5/index.cgi?bareword_uppercase_filehandles
Telemachus
+1  A: 

You should use lexical file handles and the three argument form of open as well as avoiding slurping files unnecessarily.

In any case, the most likely reason for your problem is a single character missing from your pattern. Compare the one below with the one you have above.

#!/usr/bin/perl

use strict;
use warnings;

my @value;
my $find= qr/^\s+([0-9]+)\s+([A-Z])/;

while ( my $line = <DATA> ) {
    last unless $line =~ /\S/;
    push @value, join '', $line =~ $find;
}

use Data::Dumper;
print Dumper \@value;

__DATA__
   1389 E not
   188  S yes
   24   D yes
   456  K not
   2    Q yes
Sinan Ünür
@Sinan: my bad, of not using '\s+' instead of '\s'. But, still first value is being printed on printing the first element of array.
shubster
@shubster Figure out what's different between the code/data above and the code/data you don't show us.
Sinan Ünür
A: 

Do you have leading whitespace lines, or other leading lines in your data that don't match your regexp? Since you're unconditionally push()-ing onto your output array, regardless of whether your regexp matches, you'll get blank array elements for every non-matching line in your input.

Observe:

#!/usr/bin/perl

use strict;
use warnings;

my @lines;

while (<DATA>) {
  push @lines , ( join( '' , /^\s*(\d+)/ ));
}

foreach ( 0 .. $#lines ) {
  print "$_ -> $lines[$_]\n";
}


__DATA__
FOO
Bar
Baz
   1234
456
bargle

Output:

0 -> 
1 -> 
2 -> 
3 -> 1234
4 -> 456
5 ->
genehack