6

Is there a simple way to separate a very large number per thousands with printf, awk, sed ?

So 10000000000000 become 10 000 000 000 000

Thanks

  • Do you want to format a string with just an integer number or is this a text file with many numbers or ...? – Philippos Jan 10 '18 at 9:14
  • You're not satisfied with unix.stackexchange.com/questions/249116/… ? – Gerard H. Pille Jan 10 '18 at 9:15
  • @Philippos, any number (floats or intergers) are to format – rcspam Jan 11 '18 at 11:38
  • @GerardH.Pille, no this is not exhaustive. – rcspam Jan 11 '18 at 11:39
  • If you want a float like 12345.6789 formatted as 12 345.678 9, the story gets more complicated ... – Philippos Jan 11 '18 at 14:27
3

A simple combination of sed and rev could be employed -

echo  "I have 10000013984 oranges" | rev | sed "s/[0-9][0-9][0-9]/& /g" | rev 

first rev is required to replace number from right to left , and the second one for bringing back the original string.

  • 1
    That inserts a space before 999 in I have 999 oranges though. – Stéphane Chazelas Jan 10 '18 at 12:03
  • @StéphaneChazelas, thanks, i have found another solution without rev echo 10000013984.23 | sed ':a;s/\B[0-9]\{3\}\>/ &/;ta' this could be enough with floats with maximum 3 decimals after the decimal point – rcspam Jan 11 '18 at 11:42
  • Why only 3 votes on this accepted answer, while 12 votes on the other answer? – user May 31 at 4:12
12

With some printf implementations (including GNU printf and the printf builtin of ksh93, zsh, bash and lksh (but not dash nor yash) on GNU systems) and assuming your system has a French (of France or Canada at least), or Swedish or Slovenian or Macedonian or Kyrgyz locale (and a few more, that is, those that have space as the thousand separator):

$ LC_ALL=fr_FR locale -k thousands_sep
thousands_sep=" "
$ LC_ALL=fr_FR printf "%'d\n" 10000000000
10 000 000 000

Also works with some awk implementations:

$ LC_ALL=fr_FR awk 'BEGIN{printf "%'\''d\n", 1e10}'
10 000 000 000

You can use LC_NUMERIC instead of LC_ALL if you know LC_ALL is otherwise not set.

  • how did you get rid of the dots or comma's ? – Gerard H. Pille Jan 10 '18 at 9:32
  • @GerardH.Pille, the thousand separator is space in French (also in Slovenian and Swedish). See the output of LC_ALL=fr_FR locale -k thousands_sep – Stéphane Chazelas Jan 10 '18 at 9:33
  • Indeed it is, I only had fr_BE to test with, sorry! – Gerard H. Pille Jan 10 '18 at 9:36
  • that's the case when the Frenchman knows what he's talking about ) – RomanPerekhrest Jan 10 '18 at 9:58
5

You need to loop with sed:

echo "9765625000 * 1024 = 10000000000000" | sed -e :a -e 's/\(.*[0-9]\)\([0-9]\{3\}\)/\1 \2/;ta'
9 765 625 000 * 1 024 = 10 000 000 000 000

(Thank you for the improvement, Stéphane!)

Or better readable, but maybe not compatible with older sed versions, with extended regular expressions:

echo "9765625000 * 1024 = 10000000000000" | sed -E -e :a -e 's/(.*[0-9])([0-9]{3})/\1 \2/;ta'
9 765 625 000 * 1 024 = 10 000 000 000 000
  • 1
    Or POSIXly sed -e :1 -e 's/\(.*[0-9]\)\([0-9]\{3\}\)/\1 \2/;t1' – Stéphane Chazelas Jan 10 '18 at 9:54
  • Although it's just a matter of time to include -E for ERE into the standard, I have to admit that your solution to take advantage of the greedy .* is far more elegant than my clumpy ([^0-9]|$)! – Philippos Jan 10 '18 at 11:12
  • More simple: sed ':a;s/\B[0-9]\{3\}\>/ &/;ta' – rcspam Jan 11 '18 at 11:47
  • @rcspam Correct, but only for GNU sed. \B expands POSIX, :a; violates POSIX. – Philippos Jan 11 '18 at 14:23
2

If you're happy with the thousands separator defined by the current locale, you can use the perl Number::Format module like this:

$ perl -e 'use Number::Format qw(format_number);
           print format_number(shift),"\n"' 10000000000000
10,000,000,000,000

(extra linefeeds added for readability. this can be entered all on one line)

If you want spaces separating the thousands regardless of locale:

perl -e 'use Number::Format;
         my $num= new Number::Format(-thousands_sep=>" ");
         print $num->format_number(shift), "\n"' 10000000000000
10 000 000 000 000
1

with LANG=nl_BE

awk 'BEGIN {
  I=0
  T=1
  while ( I < 10 ) {
    S = sprintf("%'\''d",T)
    gsub(/\./," ",S)
    print S
    T *= 10
    I++
  }
}'

gives

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

Thanks to all,

I have found a solution that suits me with sed. It works with integers and floats (with less than 3 decimals).

echo 12120013984.235 |  sed ':a;s/\B[0-9]\{3\}\>/ &/;ta'

I have found it here: http://shallowsky.com/blog/linux/cmdline/sed-insert-commas.html with the explanations:

\B Matches anything that is not a word boundary.

[0-9] Matches any digit.

\{3\} Matches three repetitions of whatever precedes it (in this case, a digit).

\> Matches a word boundary at the end of a word.

  • That's GNU-specific though (contrary to @Philippos' similar one). – Stéphane Chazelas Jan 11 '18 at 12:04

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.