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

Dear Monks,

I am a newbie in perl, and have problem with my script as it skips my first input line when I'm using while. I am not sure how to fix it as I have looked up some examples, but still couldn't solve the problem. Here is the code,

#! /usr/bin/perl $file_input = "input"; open(IN, $file_input) || die "can't open $file_input : $!\n"; while(<IN>) { $line =$_; if($line =~ /^(.*) (\d+)$/) { $data[$#data ++ ] = $1; } } close(IN); $file_output = "output"; open(OUT, ">".$file_output) || die "can't open $file_output : $!\n"; for($i = 0; $i <= $#data; $i ++) { print OUT $data[$i]. "\n"; } close(OUT);
Here is the example of my input file. Using the perl script that I wrote, I basically want to remove the second column of the input file.
47 58 320 52 410 59 1 200 42 58
and when I run perl script, the first line dissapears. Hope you can help. Thank you in advance.

Replies are listed 'Best First'.
Re: using while, first line was skipped
by choroba (Cardinal) on Jul 27, 2015 at 14:58 UTC
    Why did you use
    $#data ++

    The normal way to add a new member to an array is to use push:

    #!/usr/bin/perl use warnings; use strict; my @data; while (my $line = <>) { if ($line =~ /^(.*) (\d+)$/) { push @data, $1; } } for my $d (@data) { print "$d\n"; }

    Update: When @data is empty, $#data ++ returns -1, but for some reason, and creates one empty element in the array (same as $#data = 0), so Perl doesn't die with

    Modification of non-creatable array value attempted, subscript -1

    as it does for

    my @data; $data[-1] = 2;

    In the next iteration of the loop, $#data is 0 (and so is $#data ++), so the assignment overwrites the first assigned value.

    لսႽ† ᥲᥒ⚪⟊Ⴙᘓᖇ Ꮅᘓᖇ⎱ Ⴙᥲ𝇋ƙᘓᖇ
      Hi choroba, Thank you for helping out and for your explanation. I use $#data ++ because I just followed some example I found. Is this how I supposed to write again?
      #! /usr/bin/perl my $file = 'input'; open my $fh, '<', $file or die "cant open : $!"; @data=<$fh>; while (my $line = <>) { if ($line =~ /^(.*) (\d+)$/) { push @data, $1; } } for my $d (@data) { print "$d\n"; } close $fh;
      It doesn't work though. Do you mind show me a detailed code that I could possibly implement? Thank you!
        I use $#data ++ because I just followed some example I found.

        Dangerous approach. You might get bitten if you use code you don't understand. And this example particularly is very obscure - please avoid that source of examples.

        Let's take a close look at this piece of code:

        $data[ $#data ++ ] = $1;

        What happens here, and why does it work, but not as expected?

        Consider:

        • An empty array has no elements.
        • The index -1 references the last element of an array.
        • $#data is the index of the last element of @data.
          • If @data has 1 element, it is 0.
          • If @data has no elements, it is -1.
        • Assigning to the last element of an empty array (which doesn't exist) gives a runtime error:
          Modification of non-creatable array value attempted, subscript -1 at ...
        • Array elements can be pre-allocated assigning to $#data.
        • $#data ++ is a post-increment.

        Putting the above pieces together, the following happens the first time through the loop:
        The last element of the empty array @data is requested by evaluating $#data, which yields -1. Finishing the evaluation of the term inside brackets [ $#data ++ ], the value of $#data is incremented after establishing the index requested, which leads to the creation of the first array element which index 0, so accessing the last element (index -1) is valid, and $1 is assigned to the first slot (which happens to be the last) at index 0.

        The next time through the loop, $#data yields 0, after that it is incremented, creating a slot at index 1. The assignment is also to the element at index 0, which means its contents from the previous assignment is overwritten.

        After the loop, @data has a dangling empty array element, and the first value is gone. Had you used warnings, you would have seen that:

        use warnings; my @data; for( 1..3 ) { $data[ $#data ++ ] = $_; } for (@data) { print $_,"\n"; } __END__ 2 3 Use of uninitialized value $_ in print at - line 7.

        Perl tries hard to do what you mean... if you know what you mean.

        Changing the post-increment to a pre-increment yields the desired effect:

        use warnings; my @data; for( 1..3 ) { $data[ ++ $#data ] = $_; } for (@data) { print $_,"\n"; } __END__ 1 2 3

        - but it is unwieldy, and as choroba above says, better use push which is the preferred idiom.

        perl -le'print map{pack c,($-++?1:13)+ord}split//,ESEL'
        No. Just use the file handle instead of the default one:
        while (my $line = <$fh>) {
        لսႽ† ᥲᥒ⚪⟊Ⴙᘓᖇ Ꮅᘓᖇ⎱ Ⴙᥲ𝇋ƙᘓᖇ
Re: using while, first line was skipped (invisble)
by tye (Sage) on Jul 27, 2015 at 14:56 UTC

    That could be explained by something as simple as trailing whitespace. Other "invisible" characters could also be the cause. Look at your file contents more closely, for example, using od.

    - tye        

      Thank you for your comment! :)

      Yes, but we really hope it isn't don't we, otherwise Perl would be harder to use! We rather hope instead that it's explained by the use of postfix ++ later on in the code. (Update: as has already been addressed by now)

      One world, one people

        That could be explained by something as simple as trailing whitespace.
        Yes, but we really hope it isn't don't we, otherwise Perl would be harder to use!

        Perhaps you did not understood my comment? Whether it was actually trailing whitespace (in this case) or not does not at all change the fact that the regex used (and surrounding code) did not handle something as mundane as trailing whitespace. There being no trailing whitespace in the particular input file being used has no bearing on how hard it is to use Perl. Perl actually does not magically ignore trailing whitespace in input for you. (Perl does do several things to try to almost magically deal with newlines in your input for you.)

        Since there were other problems with the code that certainly result in the first line being ignored, the odds are good that the first two lines didn't have trailing whitespace, else the problem statement would have been different, of course.

        - tye