使用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中工作。
答案 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)