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

Perl Monks,

I currently have this simplistic script:

open( INFILE, "<$filename" ) || die "$!\n"; open( OUTFILE, ">>dataload.errs" ) || die "$!\n"; while ( $line = <INFILE> ) { if ( "***M" eq substr $line => 0, 4 ) { print OUTFILE $line; } if ( "!!!" eq substr $line => 0, 3 ) { print OUTFILE $line; } if ( "@@@" eq substr $line => 0, 3 ) { print OUTFILE $line; } } close( INFILE ); close( OUTFILE );

This goes through and strips out the lines that start with certain characters. I am happy with that (although any pointers are gladly appreciated).

Now I want to put the total of lines that were written for each group at the end of the file like:

Total errors for @@@: 3
Total errors for ***: 5
Total errors for !!!: 9

Your wisdom and help will be appreciated!

Robert

Replies are listed 'Best First'.
Re: Counting lines as I write them...
by Zaxo (Archbishop) on Apr 11, 2005 at 14:30 UTC

    You can keep seperate counts in a hash,

    my %count; @count(qw/@@@ *** !!!/) = (0) x 3;
    Wherever you find a match, increment the corresponding key,
    if ( "!!!" eq substr $line => 0, 3 ) { $count{'!!!'}++; print OUTFILE $line; }
    and so on. To print out the results,
    printf "Total errors for %s: %d\n", $_, $count{$_} for keys %count;

    After Compline,
    Zaxo

Re: Counting lines as I write them...
by Fletch (Bishop) on Apr 11, 2005 at 14:25 UTC

    So count them as you print them.

    my %count; ## ... if( "!!!" eq substr $line => 0, 3 ) { $count{ "!!!" }++; print OUTFILE $line; } ## ... print "Total errors for $_: $count{ $_ }\n" for qw( @@@ *** !!! );

    Update: And no one else has mentioned it, but you might want while( defined( $line = <INFILE> ) ) { ... } if 0\n might occur in your input.</pedant>

      Regarding your pedant ...

      use strict; use warnings; use diagnostics; while (my $line = <DATA>) { print $line; } __DATA__ 0 1 2 3 0
      (Note that the last line does not have a carriage return on it - so the last character in the file is '0'.)

      Running this with perl 5.8.6 seems to net me all 5 lines (and no carriage return on the last line, leaving my prompt shifted over by one). perlsyn refers to this behaviour (i.e., while implicitly checking definedness rather than truth for file reads) rather off-the-cuff in its examples, but I'm having a hard time finding where this is explicit.

        Carp, now I can't remember when that changed. That was the behavior ages ago, and then I think you're right that there was an implicit defined-ness check stuck in sometime. I want to say I hit that with an older (5.6 era) perl sometime recently but I can't reproduce it with 5.6.1.

        Probably just best to remember it can happen in case you're ever messing around with a really old version, and not worry about it otherwise.

Re: Counting lines as I write them...
by fglock (Vicar) on Apr 11, 2005 at 14:32 UTC
    use strict; my $filename = shift; open( INFILE, "<$filename" ) || die "$!\n"; open( OUTFILE, ">>dataload.errs" ) || die "$!\n"; my %count; my $line; while ( $line = <INFILE> ) { if ( $line =~ /^( \*{3}M | \!{3} | \@{3} )/x ) { $count{$1}++; print OUTFILE $line; } } close( INFILE ); close( OUTFILE ); print "Total errors for $_: $count{$_}\n" for keys %count;
Re: Counting lines as I write them...
by sh1tn (Priest) on Apr 11, 2005 at 14:23 UTC
    Update: syntax errors - '$' missing
    open( INFILE, "<$filename" ) || die "$!\n"; open( OUTFILE, ">>dataload.errs" ) || die "$!\n"; my %total; while ( $line = <INFILE> ) { if ( "***M" eq substr $line => 0, 4 ) { print OUTFILE $line; $total{'***'}++ } if ( "!!!" eq substr $line => 0, 3 ) { print OUTFILE $line; $total{'!!!'}++ } if ( "@@@" eq substr $line => 0, 3 ) { print OUTFILE $line; $total{'@@@'}++ } } close( INFILE ); close( OUTFILE ); print "$_ $total{$_}\n" for keys %total
    Update:
    print "Total errors for $_: $total{$_} \n" for keys %total


      total{'***'}++ et al will work much better (and in fact will actually compile) with the correct leading sigil ($) . . .

Re: Counting lines as I write them...
by davidrw (Prior) on Apr 11, 2005 at 16:22 UTC
    fglock's method seems like one of the better ways, but here's an alternative if a filter is more desirable (good one-liner example if nothing else):
    perl -ne '++$cts{$1} && print if /^(\*{3}M|\!{3}|\@{3})/; END { while( +my($k,$v)=each %cts){printf STDERR "Total errors for %s: %d\n", $k, $ +v;} }'
    can use it with anything along the lines of (note bash syntax for redirects):
    $ONELINER somelog.txt >> dataload.errs 2> cts.txt cat somelog.txt | $ONELINER >> dataload.errs
    where "$ONELINER" represents the perl script pasted in, or an alias, or a 1 line script file w/the perl.