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

Hi Monks , I have this input file which I am trying to read and capture some lines based on the user input:
01 - three cans - one file - three balls 02 - none - yes coffee 03
the user will enter a number , somthing like "01" then what I need is to read that file and prints the comments right under it which in this case will be as follow:
- three cans - one file - three balls
My code caputre only the first comment I am not sure why:
my $number; my $file = "INFO\\comments.txt"; my $copyfile = "INFO\\commentsCopy.txt"; open(FILE, "<", $file ) or die "Cannot read '$file': $!"; open(COPYFILE, ">INFO\\commentsCopy.txt" ) or die "Cannot read 'commen +tsCopy.txt': $!"; my $found=0; while (<FILE>){ if ($found) { if (/^$number/) { if (/^-/) { print COPYFILE "$_\n"; $found = 0; } } } elsif (/^$name/o) { $found = 1; } } close (COPYFILE); close (FILE);
Do you see the problem ? thanks for any help.

Replies are listed 'Best First'.
Re: regex issue
by BUU (Prior) on May 11, 2004 at 04:30 UTC
    Bored:
    my $number = '01'; while(<DATA>) { print if ( (/$number/ ... /^\d\d/) =~ /^\d(?!E0)/); } __DATA__ 01 - three cans - one file - three balls 02 - none - yes coffee 03 - foo - bar
      My reading of the question is a bit different. Shouldn't that be      print if /$number/ ... /^\d+/ and /^-/; and thus not print out any number, just the lines starting with '-'?

      And as a little explanation of BUU's rather unusal construct (the 2nd half, not the '...' operator itself), a little excerpt from perlop:

      The value returned [by the '..'/'...' operator] is either the empty string for false, or a sequence number (beginning with 1) for true. The sequence number is reset for each range encountered. The final sequence number in a range has the string "E0" appended to it, which doesn't affect its numeric value, but gives you something to search for if you want to exclude the end-point.
      That is what is being tested for with /^\d(?!E0)/. It also becomes clear, that there is a subtle bug lurking here if there are 10 or more lines. To fix that, the regex has to read /^(?>\d+)(?!E0)/.

      -- Hofmator

      BUU, could you explain what your code is doing? I've not seen a construct like this before, and so I have no idea how it works.

      Thank you for any enlightenment you can provide.

        You'll find information about "..." in perlop:

        In scalar context, ".." returns a boolean value. The operator is bistable, like a flip-flop...

        (I doubt I can explain this particular construct in any sort of coherent manner, so I'll leave that to BUU.)

Re: regex issue
by davido (Cardinal) on May 11, 2004 at 04:32 UTC
    This seems to do the trick. I took the liberty of reading from <DATA> instead of your input filehandle, and writing just to standard output instead of to a filehandle. I just did that for the sake of making the solution easy to read. You can easily adapt it to read from any filehandle and print to any other filehandle.

    my $number = 02; while ( <DATA> ) { last if /^$number/; } while ( <DATA> ) { last if /^\d+/; print if /^-/; } __DATA__ 01 - three cans - one file - three balls 02 - none - yes coffee 03

    I hope this helps...


    Dave

      my $number = 02;

      I can only get your code to work by quoting $number, like so:

      my $number = '02';

      presumably because perl converts "02" (unquoted) to a standard number...

      dave

      thanks all ,, thats a lot of help. :)
Re: regex issue
by exussum0 (Vicar) on May 11, 2004 at 04:29 UTC
    You have, /^$number/ but you never assign it. But more important, you match on ^-, and then change found back to 0 after printing a line, which so happens to be the first. Might i suggest..
    my $found=undef; while (<FILE>){ if ($found eq $number and /^-/) { print COPYFILE "$_\n"; } elsif (/^($name)/o) { $found = $1; } } close (COPYFILE); close (FILE);

    Untested, mother aproved. ;)

Re: regex issue
by graff (Chancellor) on May 12, 2004 at 02:19 UTC
    My code caputre only the first comment I am not sure why:
    while (<FILE>) { ## (note: using logical indenting) if ($found) { if (/^$number/) { if (/^-/) { print COPYFILE "$_\n"; $found = 0; ## <<== this is why } } } elsif ... }
    You are using a "state variable" ($found) to tell the loop whether a given line should be printed, based on whether the user-specified $name has been seen. But as soon as you print out one line, you reset $found to 0 -- only one line can be printed after $name occurs.

    Actually, looking at it just now, and not knowing how you are setting $number, it's just as likely that it would never print anything at all, unless $number is always assigned some string that starts with "-" (or is never assigned any value at all, which seems more likely in this case).

    My point is that you are applying two regex tests, one right after the other, that both check whether something particular occurs at the beginning of the line. That's a bit silly.

    I think what you were trying to shoot for (but missed) was something like this:

    $found = 0; while (<FILE>) { if ( $found ) { if ( /^\d+/ ) { # does line start with a digit? $found = 0; # then we're done printing stuff } elsif ( /^-/ ) { # otherwise, if there's stuff print COPYFILE; # then we print it } } elsif ( /^$name/ ) { $found = 1; } }
    Anyway, I think some of the other proposals above are cleaner. As a closing comment, let me suggest that using consistent indentation will help.