Category: | Text Processing |
Author/Contact Info | John Clyman (module-support@clyman.com) |
Description: | Create an object that will pretty-format numbers using SI prefixes, your preferred truncation behavior, and so on. For example, return a number like '12' as '12 bytes' and a number like 1048976 as '1 MB'. |
package ScaleUnits; $VERSION = '0.01'; use strict; use warnings; use Carp; my @si_prefixes = qw(kilo mega giga tera peta exa zetta yotta); sub new { my $class = shift; my %args = @_; croak "ScaleUnits::new requires named parameter 'unit'\n" unless defined $args{unit}; %args = (plural => $args{unit} . 's', factor => 1000, round => 0, brief => '', separator => ' ', %args); my $self = \%args; bless $self, $class; return $self; } sub scale { my ($self, $value) = @_; return scale_units($value, $self->{unit}, $self->{plural}, $self->{factor}, $self->{round}, $self->{brief}, $self->{separator}); } sub scale_units { my ($value, $base_unit, $plural, $factor, $round, $brief, $separator +) = @_; my $order = 0; while ($value >= $factor && $order <= $#si_prefixes) { $value /= $factor; $order++; } $value = int($value*(10**$round))/(10**$round); # if $order is 0, the number doesn't need a prefix # otherwise it needs a prefix. by default ($brief is ''), we use the # full SI prefix. If $brief contains something, we instead use the s +hort # and capitalized form of the SI prefix (e.g. 'mega' becomes 'M'). my $si_prefix = ($order == 0 ? '' : $brief eq '' ? $si_prefixes[$order-1] : uc substr($si_prefixes[$order-1],0,1)); # if $brief is set and we do have a prefix ($order >0), then use $br +ief # as the units. Otherwise we have to decide between singular and plu +ral. my $units = ($brief ne '' && $order > 0 ? $brief : $value == 1 ? $base_unit : $plural); return "$value$separator$si_prefix$units"; } 1; __END__ =head1 NAME ScaleUnits - Perl class that takes numbers and formats them nicely, wi +th SI prefixes, user-selectable rounding, and more =head1 SYNOPSIS use ScaleUnits; $converter = ScaleUnits->new( unit => 'byte', factor => 1024, round => 2 ); print $converter->scale(123_456_789); # '117.73 megabytes' =head1 DESCRIPTION You want your program to generate nice readable output like this: Transferred 12 bytes Transferred 2.14 gigabytes instead of this: Transferred 12 byte(s) Transferred 2243953 byte(s) Specifically, C<ScaleUnits> can apply SI prefixes from 'kilo' (10**3, +or 2**10 if you wish) through 'yotta' (10**24). It can round numbers to the num +ber of decimal places that you specify, and apply singular and plural forms a +s appropriate. It can also use abbreviated forms such as 'MB' and 'GB'. You define the desired behavior when you construct a C<ScaleUnits> obj +ect, and then each time you call the C<scale> method with a specific number, th +e appropriate rules are applied and a nicely-formatted string is returne +d. =head1 PUBLIC METHODS =head2 new(%params) Create a new C<ScaleUnits> object. Pass parameters by name. A C<unit> parameter is required; others are optional. =over 4 =item unit The name of the units you are scaling. Use the singular form: 'byte', 'minute', and so on. By default, C<ScaleUnits> will construct a plural form by adding an 's' to this base unit, but you can override this behavior by passing a C<plural> argument. =item plural The plural of the units you are scaling. By default, C<ScaleUnits> creates the plural form of a unit by appending a single 's'. =item factor The factor you want to use between successive levels of the SI prefix hierarchy. By default, this is 1,000. For scaling bytes and other base-2 numbers, you may want to make it 1,024: $s = ScaleUnits->new(unit => 'byte', factor => 1024); Now 'kilo' will be used for a factor of 1,024, 'mega' for a factor of 1,048,976, and so on. =item round Round scaled numbers to this many decimal places (maximum). By default +, it's 0, and all results will be in whole numbers. =item brief Set C<brief> to the units you want to use when a number is large enoug +h to require an SI prefix. This will also cause the single-letter form o +f SI prefixes to be used. A typical use would be: ScaleUnits->new(unit => 'byte', brief => 'B'); This will result in output like this: 35 bytes 1 MB 4 GB By default, C<brief> is a null string, which means that the full SI pr +efixes are prepended onto the unit name (singular or plural as needed). =item separator Defines the separator that appears between the scaled number and its accompanying units. By default, it's a space, so results will read like '24 kilobytes'. By setting C<separator> to a null string you can get results like '24KB' (no space). Setting C<separator> to a hyphen c +ould be useful for generating adjectival forms ('You uploaded a 24-kilobyte + file'). =back =head2 scale($number) Scale C<$number> using the settings defined when the object was constr +ucted, and return the results as a string. =head1 SEE ALSO If you want to do conversions between different units -- e.g., meters +to inches -- you'll probably want to use a module like C<Math::Units> or C<Convert::Units> instead. If you want to handle singular and plural forms for arbitrary terms, t +ry C<Lingua::EN::Inflect>. =head1 BUGS AND LIMITATIONS Currently provides prefixes only for positive exponents -- there's no +provision for converting a fractional number into milli-units, micro-units, and +so on. Doesn't currently provide a provision for using the alternate SI prefi +xes, such as 'kibi', that I've read have been proposed for base-2 numbers. =head1 RELEASE AND REVISION HISTORY 0.01 - 4 October 2002 - first release =head1 AUTHOR AND COPYRIGHT INFORMATION (C) Copyright 2002 John Clyman (Z<>module-support@clyman.comZ<>, http://www.clyman.comZ<>). All Rights Reserved. This program is free software and may be used and redistributed under +the terms of the Artistic License. =cut |
Back to
Code Catacombs