#!/usr/bin/perl -w # (C) 2003-2007 Willem Jan Hengeveld # Web: http://www.xs4all.nl/~itsme/ # http://wiki.xda-developers.com/ # # $Id: add 1896 2008-06-30 10:55:03Z itsme $ # use strict; # this script tries to split the input in colums, and add # the numbers or expressions found either by row, or by column. # todo: add option to add times, hh:mm:ss or degrees deg 'min ''sec my $doAddPerRow= 0; my $doAddPerColumn= 1; my $ColumnSeparator= "\\s+"; my $StripLeadingSpace= 1; my $doEval= 0; my $doStripQuotes= 0; my $anchorednrs= "^\\s*"; my $doExtractScale= 0; use Getopt::Long; Getopt::Long::Configure ("bundling"); sub usage { return <<__EOF__ Usage: add [options] [files] -r print row totals too -c [default] print column totals -q strip quotes -e evaluate cells ( allows you to put numerical expressions in cells -m extract numbers embedded in text, otherwise only pure number cells are used -l don't strip leading spaces -s extract scale ( kilo, Mega, etc ) -t RE specify column separator __EOF__ } GetOptions( "r" => \$doAddPerRow, "c" => \$doAddPerColumn, "q" => \$doStripQuotes, "e" => \$doEval, "m" => sub { $anchorednrs= ""; }, "l!" => \$StripLeadingSpace, "s" => \$doExtractScale, "t=s" => sub { $ColumnSeparator= parseColumnSeparator($_[1]); }, ) or die usage(); sub parseColumnSeparator { my ($cs)= @_; if ($cs =~ /\/(.*)\//) { return qr($1); } else { return eval('"$cs"'); } } if (!$doAddPerRow && !$doAddPerColumn) { die "did not specify how to sum\n"; } my $outputseparator; my @sums; my @decimals; while(<>) { chomp; s/^\s+// if ($StripLeadingSpace); if (!defined $outputseparator) { if (/$ColumnSeparator/) { $outputseparator = $&; } } my @cols= split($ColumnSeparator, $_); # todo: extract separators, to be able to print result # with correct separators. if ($doStripQuotes) { @cols= map { if (/^'(.*)'$/) { $1; } elsif (/^"(.*)"$/) { $1 } else { $_ } } @cols; } for my $col (0..$#cols) { # if ($cols[$col] =~ /\d([,.])\d\d\d(?:[.,])\d/) { # $thousandsep[$col]{$1}++; # $decimalsep[$col]{$1 eq "."?",":"."}++; # } # elsif ($cols[$col] =~ /\d([.,](?:\d{1,2}|\d{4,})\b/) { # $decimalsep[$col]{$1}++; # $thousandsep[$col]{$1 eq "."?",":"."}++; # } $cols[$col] =~ s/,//g; # strip 1000's separator if ($cols[$col] =~ /\.(\d+)/ && (!defined $decimals[$col] || length($1) > $decimals[$col])) { $decimals[$col] = length($1); } } if ($doEval) { @cols= map { my $res=eval $_; defined $res ? $res : $_ } @cols; } if ($doAddPerRow) { printf("%10s :%s\n", sum(@cols), $_); } if ($doAddPerColumn) { $sums[$_]+= extract_number($cols[$_]) for (0..$#cols); } } if (!defined $outputseparator) { $outputseparator= ""; } if ($doAddPerColumn) { if ($doAddPerRow) { printf("%10s :", sum(@sums)); } for my $col (0..$#sums) { print $outputseparator if ($col); printf("%.*f", $decimals[$col], $sums[$col]); } printf("\n"); } sub sum { my $sum=0; $sum+= extract_number($_) for (@_); return $sum; } sub get_scale { return 1 if (!defined $_[0] || length($_[0])==0); my $i= index("yzafpnum.kMGTPEZY", $_[0]); return 1000**($i-8) if ($i>=0); return 0.1 if $i eq 'd'; return 0.01 if $i eq 'c'; return 100 if $i eq 'h'; return 1; } sub extract_number { if ($_[0] =~ /$anchorednrs(-?\d+(?:\.\d+)?(?:[eE]\d+)?)(?:\s*([yzafpnumcdhkMGTPEZY]))?/) { return $1*($doExtractScale?get_scale($2):1); } return 0; } exit(0);