Is there a function which does grouping numbers (t

2019-05-09 22:44发布

问题:

Is there somewhere hidden in a (small) module a function, which does this for me:

my $var = 23654325432;
$var = reverse $var;
$var =~ s/\d{3}\K(?=\d+)/_/g;
$var = reverse $var;

I like Number::Format but it didn't pass all tests on windows.

use Number::Format;
my $nf = new Number::Format( 
    -thousands_sep   => ',',
    -decimal_point   => '.',
);

my $formatted = $nf->format_number( 23142153 );

回答1:

Look at perl5i. It has commify and group_digits methods.



回答2:

The fastest method is in the perlfaq ( perldoc -q commas ):

sub commify {
    local $_ = shift;
    1 while s/^([-+]?\d+)(\d{3})/$1,$2/;
    return $_;
}

Of course, I just came up with a function that beats this one by ~70%.

use constant THOU_SEP => ',';
use English qw<@LAST_MATCH_START @LAST_MATCH_END>;
sub ss_commify { 
    my $s = shift;
    return $s unless $s =~ m/\d{4,}/;
    my ( $f, $p ) = ( $LAST_MATCH_START[0], $LAST_MATCH_END[0] );
    my $ts = THOU_SEP;
    # |-- That is, replace a *0-length* substring at $p with the 
    # v   thousand separator
    substr( $s, $p, 0, $ts ) while ( $p -= 3 ) > $f; 
    return $s;
}

And it works with whatever text you have in front or back. The FAQ version just works with text in back (admittedly a numeric value in Perl).

The idea is find what you want to work on with the regex once and then simply use Perl's elastic string implementation to do the perfunctory work. A substitution for each of these seems to be a bit more overhead than necessary.



回答3:

I use

$num =~ s/(\d) (?= (?:\d{3})+ \b )/$1,/gx;

which just adds a comma after every digit that is followed by a multiple of three digits.

If you'd like a function instead:

sub commify {
  (my $num = $_[0]) =~ s/(\d) (?= (?:\d{3})+ \b )/$1,/gx;
  $num;
}

print commify(10 ** $_), "\n" for 1 .. 14;

OUTPUT

10
100
1,000
10,000
100,000
1,000,000
10,000,000
100,000,000
1,000,000,000
10,000,000,000
100,000,000,000
1,000,000,000,000
10,000,000,000,000
100,000,000,000,000