#!/usr/bin/perl -w use POSIX 'round'; use Getopt::Std; $Getopt::Std::STANDARD_HELP_VERSION++; $VERSION = "0.0.1"; ($SELF = $0) =~ s,.*/,,; sub VERSION_MESSAGE { print "$SELF $VERSION\n"; } sub HELP_MESSAGE { print <, licensed WTFPL. EOF } # This regular expression doesn't recognize numbers without # a digit before the decimal point, such as .01 or -.123. However, # before this is used, the caller puts a 0 in the right place for # such numbers. # Doesn't the regex syntax with /x and comments remind you of # assembly language? $re = qr { ( # EITHER: (?: # hex number, 0[xX] # leading 0x or 0X required, [0-9a-fA-F]+ # hex digits, case insensitive. ) | # OR: (?: # decimal number, [-+]? # with optional leading sign, \d+ # at least one digit required left of the decimal point, (?:\.\d+)? # optional decimal point with trailing digits, (?: # MAYBE: [eE] # optional "e" or "E" for scientific notation, [-+]? # optional exponent sign, \d+ # at least one exponent digit, )? # ...but the whole exponent part is optional! ) ) }x; getopts('aihmMrv', \%opt) || exit 1; VERSION_MESSAGE(), exit(0) if $opt{v}; HELP_MESSAGE(), exit(0) if $opt{h}; $opt{i} = 1 if $opt{r}; $sum = $count = 0; if($opt{a}) { for(@ARGV) { $sum += $_ for extract_numbers(); } } else { while(<>) { $sum += $_ for extract_numbers(); } } print $sum . "\n" unless $opt{M}; if($opt{m} || $opt{M}) { if($count) { $avg = $sum / $count; $avg = to_int($avg) if $opt{i}; } else { # always print an average, even if no numbers were read. $avg = 0; } print $avg . "\n"; } exit 0; ### # Extract all the numbers in $_, return them an in array. sub extract_numbers { my @ret; # Handle numbers like .12 or -.99 by inserting a 0 before the . s/(^|\D)(\.\d)/$1 . "0" . $2/ge; for(/$re/gc) { $_ = hex $_ if /^0[xX]/; $_ = to_int($_) if $opt{i}; push @ret, $_; $count++; } return @ret; } sub to_int { warn "was: $_\n"; if($opt{r}) { $_ = round($_); } else { $_ = int $_; } warn "now: $_\n"; return $_; }