在数字中添加千位分隔符


36

在python中

 re.sub(r"(?<=.)(?=(?:...)+$)", ",", stroke ) 

用三元组拆分数字,例如:

 echo 123456789 | python -c 'import sys;import re; print re.sub(r"(?<=.)(?=(?:...)+$)", ",",  sys.stdin.read());'
 123,456,789

bash / awk怎么做?

Answers:


29

sed

$ echo "123456789" | sed 's/\([[:digit:]]\{3\}\)\([[:digit:]]\{3\}\)\([[:digit:]]\{3\}\)/\1,\2,\3/g'
123,456,789

(请注意,这仅适用于9位数字!)

或与此sed

$ echo "123456789" | sed ':a;s/\B[0-9]\{3\}\>/,&/;ta'
123,456,789

printf

$ LC_NUMERIC=en_US printf "%'.f\n" 123456789
123,456,789

我也在尝试使用awk,但最后添加了逗号echo 123456789 | awk '$0=gensub(/(...)/,"\\1,","g")'
Rahul Patil

现在我明白了,但似乎很复杂echo 123456789 | awk '$0=gensub(/(...)/,"\\1,","g"){sub(",$",""); print}'
Rahul Patil 2014年

1
首先,sed仅当数字正好是9位数字时才有效。该printf不上的zsh工作。因此,第二个sed答案可能是最好的。
帕特里克

1
@RahulPatil仅当位数为3的倍数时才能正常工作。尝试使用“ 12345678”,您将明白我的意思。
帕特里克

1
您可以做到echo 123456789 | awk '{printf ("%'\''d\n", $0)}'(显然,它并不总是在Linux上可用!?但是在AIX和Solaris上可以正常使用)
Johan

51

bashprintf支持几乎所有你可以在做printfC函数

type printf           # => printf is a shell builtin
printf "%'d" 123456   # => 123,456

printf 来自coreutils会做同样的事情

/usr/bin/printf "%'d" 1234567   # => 1,234,567

现在也支持此功能zsh此处更新了帖子。
don_crissti

1
我在bash 4.1.2上,它不支持... :(
msb

@msb似乎取决于您系统的vsnprintf。在GNU / Linux系统上,glibc似乎至少从1995
。– Mikel

2
注意printf 对当前语言环境使用千位分隔符,它可能是逗号,点或根本没有。你可以export LC_NUMERIC="en_US",如果你想强制逗号。
medmunds

通过获取支持的语言环境列表locale -a。我不得不使用en_US.utf8
享乐

7

您可以使用numfmt:

$ numfmt --grouping 123456789
123,456,789

要么:

$ numfmt --g 123456789
123,456,789

请注意,numfmt不是POSIX实用程序,它是GNU coreutils的一部分。


1
感谢您的“分组”提示。在第二个示例(--g)中,您是否要编写类似的代码,-d, --grouping因为双连字符需要较长的选项?
跳兔子

--g工作正常,我来代替--grouping,即numfmt --g 1234567890numfmt --grouping 1234567890做同样的事情。它是一个非常有用的小工具。
09:09

4
cat <<'EOF' |
13407807929942597099574024998205846127479365820592393377723561443721764030073546976801874298166903427690031858186486050853753882811946569946433649006084096
EOF
perl -wpe '1 while s/(\d+)(\d\d\d)/$1,$2/;'

产生:

13,407,807,929,942,597,099,574,024,998,205,846,127,479,365,820,592,393,377,723,561,443,721,764,030,073,546,976,801,874,298,166,903,427,690,031,858,186,486,050,853,753,882,811,946,569,946,433,649,006,084,096

这可以通过将数字字符串分成2组来完成,右侧的组为3位,左侧的组为剩余的位数,但至少为一位。然后,将所有内容替换为两组,并用逗号分隔。这一直持续到替换失败为止。选项“ wpe”用于错误列表,将语句括在带有自动打印的循环中,并将下一个参数作为perl“程序”(有关详细信息,请参见命令perldoc perlrun)。

最好的祝福...干杯


感谢匿名者的反馈。甚至是低票也可以是有用的,但只有在得到解释的情况下,请对您发现的错误发表评论。谢谢...干杯
drl

我认为此处的不足是因为您没有解释命令的作用。OP要求提供BASH/ AWK替代方案,因此他可能PERL以前没有使用过。无论如何,最好解释一下该命令的作用-特别是对于单线作业。
AnthonyK

@AnthonyK-感谢您的解释。我添加了评论以简要解释其工作原理。我认为替代解决方案通常很有用,但请注意您关于可能未使用过perl的观点...干杯
drl

我尝试了此页面上的sed和python建议。perl脚本是唯一适用于整个文件的脚本。文件中包含文本和数字。
马克

3

通过一些awk实现:

echo "123456789" | awk '{ printf("%'"'"'d\n",$1); }'  

123,456,789  

"%'"'"'d\n"是:("%单引号)(双引号)(单引号)(双引号)(单引号)d \ n"

这将为您的语言环境使用配置的千位分隔符(通常,在英语语言环境,法语空间,.西班牙语/德语...)中使用。与返回者相同locale thousands_sep


2

对我而言,一个常见的用例是修改命令管道的输出,以便用千位分隔符打印十进制数字。我宁愿使用一种可以针对Unix管道中的任何输出动态自定义的技术,而不是编写函数或脚本。

我发现printf(由Awk提供)是实现这一目标的最灵活,最难忘的方法。POSIX将单引号/单引号字符指定为用于格式化十进制数字的修饰符,它的优点是可以识别语言环境,因此不限于使用逗号字符。

当从Unix Shell运行Awk命令时,在用单引号分隔的字符串内输入单引号字符可能会很困难(以避免位置变量的Shell扩展,例如$1)。在这种情况下,我发现输入单引号字符的最可读,最可靠的方法是将其作为八进制转义序列(以开头\0)输入。

例:

printf "first 1000\nsecond 10000000\n" |
  awk '{printf "%9s: %11\047d\n", $1, $2}'
  first:       1,000
 second:  10,000,000

管道的模拟输出,显示哪些目录使用的磁盘空间最多:

printf "7654321 /home/export\n110384 /home/incoming\n" |
  awk '{printf "%22s: %9\047d\n", $2, $1}'
  /home/export: 7,654,321
/home/incoming:   110,384

如何在awk中转义单引号中列出了其他解决方案。

注意:正如在“ 打印单引号”中所警告的那样,建议避免使用十六进制转义序列,因为它们无法在不同系统上可靠地工作。


1
在此处列出的所有基于awk的答案中,这一答案无疑是最优美的(IMHO)。像其他解决方案一样,不需要像其他引号一样乱写引号。
TSJNachos117

感谢@ TSJNachos117最难的部分是记住撇号字符的八进制编码是\047
安东尼G-莫妮卡的大法官

2

awkbash具有基于的良好内置解决方案printf,如其他答案所述。但首先,sed

对于sed,我们需要“手动”执行。一般规则是,如果您有四个连续的数字,然后是一个非数字(或行尾),则应在第一和第二个数字之间插入一个逗号。

例如,

echo 12345678 | sed -re 's/([0-9])([0-9]{3})($|[^0-9])/\1,\2\3/'

将打印

12345,678

我们显然需要继续重复该过程,以便继续添加足够的逗号。

sed -re ' :restart ; s/([0-9])([0-9]{3})($|[^0-9])/\1,\2\3/ ; t restart '

在中sedt命令指定一个标签,如果上一个s///命令成功执行,则该标签将跳转到该标签。因此:restart,我使用定义了一个标签,以便其跳回。

这是一个bash演示(在ideone上),可以使用任意数量的数字:

function thousands {
    sed -re ' :restart ; s/([0-9])([0-9]{3})($|[^0-9])/\1,\2\3/ ; t restart '
}                                                 
echo 12 | thousands
echo 1234 | thousands
echo 123456 | thousands
echo 1234567 | thousands
echo 123456789 | thousands
echo 1234567890 | thousands


1

如果您正在查看BIG编号,则无法使上述解决方案起作用。例如,让我们得到一个很大的数字:

$ echo 2^512 |bc -l|tr -d -c [0-9] 13407807929942597099574024998205846127479365820592393377723561443721764030073546976801874298166903427690031858186486050853753882811946569946433649006084096

注意我需要tr从bc中删除反斜杠换行符输出。这个数字太大了,无法在awk中视为浮点数或固定位数字,我什至不想构建一个足以容纳sed中所有数字的正则表达式。相反,我可以将其反转并将逗号放在三位数的组之间,然后将其取消反转:

echo 2^512 |bc -l|tr -d -c [0-9] |rev |sed -e 's/\([0-9][0-9][0-9]\)/\1,/g' |rev 13,407,807,929,942,597,099,574,024,998,205,846,127,479,365,820,592,393,377,723,561,443,721,764,030,073,546,976,801,874,298,166,903,427,690,031,858,186,486,050,853,753,882,811,946,569,946,433,649,006,084,096


2
好答案。但是,我从来没有遇到与Awk一起使用大量数字的问题。我在许多基于Red Hat和Debian的发行版上尝试了您的示例,但是在所有情况下,Awk的大量发行版都没有问题。我对此进行了进一步的思考,我发现我尝试过的所有系统都是64位的(甚至是运行不受支持的RHEL 5的非常老的VM)。直到我测试了运行32位操作系统的旧笔记本电脑后,我才能够复制您的问题:awk: run time error: improper conversion(number 1) in printf("%'d
安东尼G-莫妮卡的大法官

1
a="13407807929942597099574024998205846127479365820592393377723561443721764030073546976801874298166903427690031858186486050853753882811946569946433649006084096"

echo "$a" | rev | sed "s#[[:digit:]]\{3\}#&,#g" | rev

13,407,807,929,942,597,099,574,024,998,205,846,127,479,365,820,592,393,377,723,561,443,721,764,030,073,546,976,801,874,298,166,903,427,690,031,858,186,486,050,853,753,882,811,946,569,946,433,649,006,084,096

如果数字中的位数是3的倍数,则这会增加一个虚假的前置逗号。
StéphaneChazelas 18年

@StéphaneChazelas:您可以获取最后一个rev命令的输出,并将其通过管道传输到sed 's/^,//g'
TSJNachos117

0

我也想有部分小数点分隔符正确分离/隔开的,所以我写这个的sed脚本,它使用一些shell变量,以适应区域和个人喜好。它还针对组合在一起的位数考虑了不同的约定

#DECIMALSEP='.' # usa                                                                                                               
DECIMALSEP=','  # europe

#THOUSSEP=',' # usa
#THOUSSEP='.' # europe
#THOUSSEP='_' # underscore
#THOUSSEP=' ' # space
THOUSSEP=' '  # thinspace

# group before decimal separator
#GROUPBEFDS=4   # china
GROUPBEFDS=3    # europe and usa

# group after decimal separator
#GROUPAFTDS=5   # used by many publications 
GROUPAFTDS=3


function digitgrouping {
  sed -e '
    s%\([0-9'"$DECIMALSEP"']\+\)'"$THOUSSEP"'%\1__HIDETHOUSSEP__%g
    :restartA ; s%\([0-9]\)\([0-9]\{'"$GROUPBEFDS"'\}\)\(['"$DECIMALSEP$THOUSSEP"']\)%\1'"$THOUSSEP"'\2\3% ; t restartA
    :restartB ; s%\('"$DECIMALSEP"'\([0-9]\{'"$GROUPAFTDS"'\}\'"$THOUSSEP"'\)*\)\([0-9]\{'"$GROUPAFTDS"'\}\)\([0-9]\)%\1\3'"$THOUSSEP"'\4% ; t restartB
    :restartC ; s%\([^'"$DECIMALSEP"'][0-9]\+\)\([0-9]\{'"$GROUPBEFDS"'\}\)\($\|[^0-9]\)%\1'"$THOUSSEP"'\2\3% ; t restartC
    s%__HIDETHOUSSEP__%\'"$THOUSSEP"'%g'
}

0

一个bash/ awk(根据要求)解决方案,无论数字的长度,如何,都可以使用thousands_sep,并且与语言环境的设置以及输入中的数字位于何处都可以使用,并且避免在以下位置添加千位分隔符1.12345

echo not number 123456789012345678901234567890 1234.56789 |
  awk '{while (match($0, /(^|[^.0123456789])[0123456789]{4,}/))
        $0 = substr($0, 1, RSTART+RLENGTH-4) "," substr($0, RSTART+RLENGTH-3)
        print}'

给出:

not number 123,456,789,012,345,678,901,234,567,890 1,234.56789

随着awk类似的实现mawk不支持间隔regex操作符,改变正则表达式来/(^|[^.0123456789])[0123456789][0123456789][0123456789][0123456789]+/

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.