Perl printf使用逗号作为千位分隔符

时间:2015-10-30 18:01:58

标签: perl

使用awk,我可以用逗号作为千位分隔符打印一个数字 (预先export LC_ALL=en_US.UTF-8)。

awk 'BEGIN{printf("%\047d\n", 24500)}'

24,500

我希望使用与Perl相同的格式,但它不会:

perl -e 'printf("%\047d\n", 24500)'

%'d

Perl Cookbook提供此解决方案:

sub commify {
    my $text = reverse $_[0];
    $text =~ s/(\d\d\d)(?=\d)(?!\d*\.)/$1,/g;
    return scalar reverse $text;
}

但是我假设因为printf选项在awk中工作,所以它也应该在Perl中工作。

11 个答案:

答案 0 :(得分:6)

撇号格式修饰符是非标准POSIX扩展名。 documentation for Perl's printf有关于此类扩展的说法

  

Perl执行自己的“sprintf”格式化:它模拟C语言   函数sprintf(3),但除了之外不使用它   浮点数,甚至只有标准修饰符   被允许。本地sprintf中的非标准扩展(3)   因此Perl不提供。

Number::Format模块将为您执行此操作,并从区域设置中获取其默认设置,因此可以是可移植的

use strict;
use warnings 'all';
use v5.10.1;

use Number::Format 'format_number';

say format_number(24500);

输出

24,500

答案 1 :(得分:5)

更多的解决方案:

$a = 12345678;
$b = reverse $a;
@c = unpack("(A3)*", $b);
$d = reverse join ',', @c;
print $d;

输出12,345,678

答案 2 :(得分:4)

大多数答案都假定格式是通用的。不是。 CLDR使用Unicode信息来解决它。 How to properly localize numbers?中有一个长线程。

CPAN具有CLDR::Number模块:

#!perl
use v5.10;
use CLDR::Number;
use open qw(:std :utf8);

my $locale = $ARGV[0] // 'en';

my @numbers = qw(
    123
    12345
    1234.56
    -90120
    );

my $cldr = CLDR::Number->new( locale => $locale );

my $decf = $cldr->decimal_formatter;

foreach my $n ( @numbers ) {
    say $decf->format($n);
    }

以下是几种运行方式:

$ perl comma.pl
123
12,345
1,234.56
-90,120

$ perl comma.pl es
123
12.345
1234,56
-90.120

$ perl comma.pl bn
১২৩
১২,৩৪৫
১,২৩৪.৫৬
-৯০,১২০

似乎很重量级,但是输出正确,您不必允许用户更改要使用的语言环境。但是,当需要更改语言环境时,就可以开始了。我也更喜欢Number::Format,因为我可以为终端或会话使用与本地设置不同的语言环境,甚至可以使用多个语言环境:

#!perl
use v5.10;
use CLDR::Number;
use open qw(:std :utf8);

my @locales = qw( en pt bn );

my @numbers = qw(
    123
    12345
    1234.56
    -90120
    );


my @formatters = map {
    my $cldr = CLDR::Number->new( locale => $_ );
    my $decf = $cldr->decimal_formatter;
    [ $_, $cldr, $decf ];
    } @locales;

printf "%10s %10s %10s\n" . '=' x 32 . "\n", @locales;

foreach my $n ( @numbers ) {
    printf "%10s %10s %10s\n",
        map { $_->[-1]->format($n) } @formatters;
    }

输出一次具有三个语言环境:

        en         pt         bn
================================
       123        123        ১২৩
    12,345     12.345     ১২,৩৪৫
  1,234.56   1.234,56   ১,২৩৪.৫৬
   -90,120    -90.120    -৯০,১২০

答案 3 :(得分:3)

我意识到这个问题来自大约4年前,但是由于它是在搜索中出现的,因此我将添加一个优雅的本地Perl解决方案。我最初是在寻找一种使用sprintf的方法,但是我发现的一切都表明它无法完成。然后,由于每个人都在滚动自己的路线,所以我认为我可以试试,这是我的解决方案。

$num = 12345678912345; # however many digits you want
while($num =~ s/(\d+)(\d\d\d)/$1\,$2/){};
print $num;

结果:

12,345,678,912,345

说明: 正则表达式对所有前导位数进行最大位数搜索。它要作用的行中的最小位数为4(1加3)。然后在两者之间添加逗号。如果在末尾(逗号前)仍有4位数字,则进行下一个循环,它将添加另一个逗号,依此类推,直到模式不匹配。

如果您需要安全使用小数点后3位以上的数字,请使用以下修改:(注意:如果您的数字没有小数点,这将无效)

while($num =~ s/(\d+)(\d\d\d)([.,])/$1\,$2$3/){};

这将确保它只查找以逗号(在上一个循环中添加)或小数结尾的数字。

答案 4 :(得分:2)

从@Laura 的回答中分离出来,我调整了纯 perl,仅正则表达式的解决方案,以适用于带小数的数字:

while ($formatted_number =~ s/^(-?\d+)(\d{3}(?:,\d{3})*(?:\.\d+)*)$/$1,$2/) {};

当然,这假定一个“,”作为千位分隔符和一个“。”作为小数点分隔符,但使用变量来说明您给定的语言环境应该很简单。

答案 5 :(得分:1)

# turning above answer into a function

sub format_float
# returns number with commas..... and 2 digit decimal
# so format_float(12345.667) returns "12,345.67"
{
        my $num = shift;
        return reverse(join(",",unpack("(A3)*", reverse int($num)))) . sprintf(".%02d",int(100*(.005+($num - int($num)))));
}

sub format_int
# returns number with commas.....
# so format_int(12345.667) returns "12,345"
{
        my $num = shift;
        return reverse(join(",",unpack("(A3)*", reverse int($num))));
}

答案 6 :(得分:1)

我使用了以下内容,但从perl v5.26.1开始不起作用

sub format_int
{
        my $num = shift;
        return reverse(join(",",unpack("(A3)*", reverse int($num))));
}

对我有用的表格是:

sub format_int
{
        my $num = shift;
        return scalar reverse(join(",",unpack("(A3)*", reverse int($num))));
}

但是要使用负数,代码必须为:

sub format_int
{
    if ( $val >= 0 ) {
        return scalar reverse join ",", unpack( "(A3)*", reverse int($val) );
    } else {
        return "-" . scalar reverse join ",", unpack( "(A3)*", reverse int(-$val) );
    }

}

答案 7 :(得分:1)

有人说 Perl 吗?

perl -pe '1while s/(\d+)(\d{3})/$1,$2/'

这适用于任何整数。

答案 8 :(得分:0)

我想以货币格式打印数字。如果事实证明,我最后还是想要一个.00。我使用了前面的例子(ty),然后用它来做更多的事情。

    sub format_number {
            my $num = shift;
            my $result;
            my $formatted_num = ""; 
            my @temp_array = (); 
            my $mantissa = ""; 
            if ( $num =~ /\./ ) { 
                    $num = sprintf("%0.02f",$num);
                    ($num,$mantissa) = split(/\./,$num);
                    $formatted_num = reverse $num;
                    @temp_array = unpack("(A3)*" , $formatted_num);
                    $formatted_num = reverse (join ',', @temp_array);
                    $result = $formatted_num . '.'. $mantissa;
            } else {
                    $formatted_num = reverse $num;
                    @temp_array = unpack("(A3)*" , $formatted_num);
                    $formatted_num = reverse (join ',', @temp_array);
                    $result = $formatted_num . '.00';
            }   
            return $result;
    }
    # Example call
    # ...
    printf("some amount = %s\n",format_number $some_amount);

我的默认mac OS X perl上没有Number库,我不想弄乱那个版本或者在这台机器上安装我自己的perl。我想我本来会使用格式化模块。

我仍然不太喜欢这个解决方案,但确实有效。

答案 9 :(得分:0)

这对钱有好处,如果你处理了数亿的话,就不断添加线条。

sub commify{
    my $var = $_[0];
    #print "COMMIFY got $var\n"; #DEBUG
    $var =~ s/(^\d{1,3})(\d{3})(\.\d\d)$/$1,$2$3/;
    $var =~ s/(^\d{1,3})(\d{3})(\d{3})(\.\d\d)$/$1,$2,$3$4/;
    $var =~ s/(^\d{1,3})(\d{3})(\d{3})(\d{3})(\.\d\d)$/$1,$2,$3,$4$5/;
    $var =~ s/(^\d{1,3})(\d{3})(\d{3})(\d{3})(\d{3})(\.\d\d)$/$1,$2,$3,$4,$5$6/;
    #print "COMMIFY made $var\n"; #DEBUG
    return $var;
}

答案 10 :(得分:-1)

产生局部输出的解决方案:

# First part - Localization
my ( $thousands_sep, $decimal_point, $negative_sign );
BEGIN {
        my ( $l );
        use POSIX qw(locale_h);
        $l = localeconv();

        $thousands_sep = $l->{ 'thousands_sep' };
        $decimal_point = $l->{ 'decimal_point' };
        $negative_sign = $l->{ 'negative_sign' };
}

# Second part - Number transformation
sub readable_number {
        my $val = shift;

        #my $thousands_sep = ".";
        #my $decimal_point = ",";
        #my $negative_sign = "-";

        sub _readable_int {
                my $val = shift;
                # a pinch of PERL magic
                return scalar reverse join $thousands_sep, unpack( "(A3)*", reverse $val );
        }

        my ( $i, $d, $r );
        $i = int( $val );
        if ( $val >= 0 ) {
                $r =  _readable_int( $i );
        } else {
                $r = $negative_sign . _readable_int( -$i );
        }
        # If there is decimal part append it to the integer result
        if ( $val != $i ) {
                ( undef, $d ) = ( $val =~ /(\d*)\.(\d*)/ );
                $r = $r . $decimal_point . $d;
        }

        return $r;
}

第一部分获取当前语言环境中使用的符号,第二部分中使用。
开始时,BEGIN块仅用于计算sysmbols。
如果由于某种原因不需要使用POSIX语言环境,则可以省略第一部分并取消注释第二部分的变量,以硬编码要使用的sysmbol($ thousands_sep,$ thousands_sep和$ thousands_sep)