#!/usr/bin/perl -w require 5.006; # i'm using "open my $fh ..." construct use strict; # ================================================= # constants # put magic constants at top, so people can easily change them my $CHARGE_CODES_FILE = "mnta2/bmp/table/smsc/chg_indicator.hdb"; my $INPUT_TEMPLATE = "A12 A4 A20 A20 A2 A2 A2 A2 A12 A4 A24 A24"; my $SOURCE_IX = 2; my $DEST_IX = 3; # ================================================= # first, load up the charge database. my %charge_codes; { open my $fh, $CHARGE_CODE_FILE or die "$0: opening '$CHARGE_CODE_FILE': $!"; while ( <$fh> ) { next if /^#/; # skip comments s/\s+$/; # remove end of line chars # get fields from line, clean them up. my ( $src, $dest, $code ) = split /,/; $src =~ s/^0+//; $dest =~ s/^0+//; $charge_codes{$src}{$dest} = $code; } } # ------------------------------------------------- # process standard input, accumulate charges. my %charges_against; while ( my $input = <> ) { # extract source and destination values my @fields = unpack $INPUT_TEMPLATE, $input; my $src = $fields[ $SOURCE_IX ]; my $dest = $fields[ $DEST_IX ]; # remove leading zeros $src =~ s/^0+//; $dest =~ s/^0+//; # see if there are any charge codes associated with this pair. my $code = $charge_codes{$src}{$dest}; if ( defined $code ) { ++$charges_against{$code}; } } # ------------------------------------------------- # output the results foreach my $code ( sort keys %charges_against ) { my $count = $charges_against{$code}; print "Total calls for Billing Code [$code] : $count \n"; } # note that skip_zero is no longer necessary. #### # the original code indicates there can never # be more than one charge code associated with # any given $src and $dest pair; if there are, # we would have to use an array ref here and # a foreach loop in the main processing. # only one charge code: $charge_codes{$src}{$dest} = $code; # # for multiple charge codes: # push @{ $charge_codes{$src}{$dest} }, $code; #### # # if you know that there is no charge code 0 or # # empty string, you could compress that like so: # if ( my $code = $charge_codes{$src}{$dest} ) # { # ++$charges_against{$code}; # } # # if you need multiple codes per src/dest pair: # my $charge_code_aref = $charge_codes{$src}{$dest} # or next; # # increment the number of charges against each code # foreach my $code ( @$charge_code_aref ) # { # ++$charges_against{$code}; # } #### # construct the template in a more self-documenting way. # this is a list of name => width values. you can add # more info here if you like; just adjust the loops # that pull information out of it. having all this # information in one place means that you only have to # change one place... my @INPUT_COLUMN_INFO = ( unknown1 => { width => 12 }, unknown2 => { width => 4 }, source => { width => 20 }, dest => { width => 20 }, ... ); # you can also use this structure to do clever things # such as building a hash out of each input line to # extract values by name instead of by numeric index. # ================================================= # derived constants my @INPUT_NAMES; # names in original order my $INPUT_TEMPLATE; # build up an 'unpack' template my %INPUT_POS; # map from name to index for ( my $i = 0; $i < @INPUT_COLUMN_INFO; $i += 2 ) { my ( $name, $info ) = @INPUT_COLUMN_INFO[$i, $i+1]; push @INPUT_NAMES, $name; $INPUT_TEMPLATE .= "A" . $info->{width}; $INPUT_POS{$name} = @INPUT_NAMES-1; } # ================================================= # use during processing while (<>) { # construct hash using slice my %input; @input{@INPUT_NAMES} = unpack $INPUT_TEMPLATE, $_; # now access info from the hash my $src = $input{source}; my $dest = $input{dest}; # ... }