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

A little new to perl and having a problem printing out the keys. Everything else in the script works fine. Does it have something to do with the fact that I'm using the default variable when I create the hash?
use strict; use warnings; my $string = 'Return code is'; my $filename = 'trace.Discovery'; my %errors =(); my $total = 0; if (open(my $fh, '<:encoding(UTF-8)', $filename)) { while (my $row = <$fh>) { chomp $row; if ($row =~ /\Q$string\E/) { my $digit = (split /\:/, $row)[-1]; $errors{$digit}++; } } } else { print "Could not open file '$filename'\n"; warn "This is the error --> $!"; } # First attempt to print keys foreach ( sort keys %errors ) { print "$_ - $errors{$_}\n"; $total = $total + $errors{$_}; } print "Total errors --> $total\n"; ### Another attempt to print the keys. while( my( $key, $value ) = each %errors ){ print "$key: $value\n"; }
Current script output.
- 1889
- 642
- 5
- 17
Total errors --> 2553
: 17
: 5
: 642
: 1889

Replies are listed 'Best First'.
Re: Can't get hash keys to print
by hippo (Archbishop) on Aug 24, 2015 at 14:19 UTC

    My guess is that the lines in your input file terminate with a carriage return, but perhaps no line feed, so the hash keys in output are effectively being overwritten by the rest of that line. Try stripping trailling carriage returns ("\r") from the lines when you read them and see if that solves it for you.

    Addendum: eg. replace the chomp $row; line with $row =~ s/[\r\n]+$//; or similar.

      It's a Windows file. The lines end with CR+LF. I'd use

      $row =~ s/\s+\z//;

      You could also "fix" the file with command line util dos2unix.

      Great catch!!! That was it. Thank you so much!
Re: Can't get hash keys to print
by Athanasius (Archbishop) on Aug 24, 2015 at 14:45 UTC

    Hello Daren, and welcome to the Monastery!

    Glad to see that hippo’s answer was what you were looking for. In the future, you should give an example of the input file, together with the output you expect from that example, and the output you actually get.

    But I want to point out that your strategy of performing a regular expression match on Return code is and then splitting on : is potentially fragile: if there is whitespace between the colon and the return code digit, this whitespace will be included in the hash key. So you could, for example, end up with separate hash entries for “7” and “ 7”. A more robust solution would be to capture the return code in the regex match:

    ++$errors{$1} if $row =~ / \Q$string\E \s* : \s* (\d+) $ /x;

    Hope that helps,

    Athanasius <°(((><contra mundum Iustus alius egestas vitae, eros Piratica,

      Athanasius,
      You are spot on, esp after the trailing carriage bit me in the butt on the keys.
      I need to get better on thinking what could break my scripts.
      Thanks for your help!
      Hello again, I understand your suggested expression, and I feel like it should work but it isn't. It isn't matching anything.
      This is a few sample input lines...
      2015/04/06 12:38:15 (9):(5564) Return code is :55
      2015/04/06 12:38:15 (9):(6600) Return code is :55
      2015/04/06 12:41:31 (9):(3604) Return code is :1

        Assuming there are any number of  space \t \r \n etc. at the end of each line, try something like:

        c:\@Work\Perl\monks>perl -wMstrict -MData::Dump -le "my $string = 'Return code is'; ;; my %errors; for my $row ( qq{2015/04/06 12:38:15 (9):(5564) Return code is :55\n\r\n}, qq{2015/04/06 12:38:15 (9):(6600) Return code is :55 \r\n\r}, qq{2015/04/06 12:41:31 (9):(3604) Return code is :1\t\n\r}, ) { ++$errors{$1} if $row =~ m{ \Q$string\E \s* : (\d+) \s* \z }xms; } dd \%errors; ;; my $total; for my $err_no (sort { $a <=> $b } keys %errors) { print qq{error $err_no - $errors{$err_no} total}; $total += $errors{$err_no}; } print qq{total errors: $total}; " { 1 => 1, 55 => 2 } error 1 - 1 total error 55 - 2 total total errors: 3


        Give a man a fish:  <%-{-{-{-<

        Something else isn't right then. Using the data you supplied above (which you should copy to your actual question for future readers), and copy/pasting Athanasius' regex, here's what I get in %errors:

        use warnings; use strict; my $string = "Return code is"; my %errors; while (<DATA>){ ++$errors{$1} if /\Q$string\E \s* : \s* (\d+) $ /x; } while (my ($k, $v) = each %errors){ print "$k: $v\n"; } __DATA__ 2015/04/06 12:38:15 (9):(5564) Return code is :55 2015/04/06 12:38:15 (9):(6600) Return code is :55 2015/04/06 12:41:31 (9):(3604) Return code is :1

        Output:

        1: 1 55: 2