web-dev-qa-db-fra.com

Ajouter un séparateur de milliers dans un nombre

En python

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

Pour diviser un nombre par triplets, par exemple:

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

Comment faire de même avec bash/awk?

42
user2496

Avec sed:

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

(Notez que cela ne fonctionne que pour exactement 9 chiffres!)

ou ceci avec sed:

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

Avec printf:

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

bash de printf prend en charge à peu près tout ce que vous pouvez faire dans la fonction C printf

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

printf de coreutils fera de même

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

Vous pouvez utiliser numfmt:

$ numfmt --grouping 123456789
123,456,789

Ou:

$ numfmt --g 123456789
123,456,789

Notez que numfmt n'est pas un utilitaire POSIX, il fait partie de GNU coreutils.

8
Steven Penny
cat <<'EOF' |
13407807929942597099574024998205846127479365820592393377723561443721764030073546976801874298166903427690031858186486050853753882811946569946433649006084096
EOF
Perl -wpe '1 while s/(\d+)(\d\d\d)/$1,$2/;'

produit:

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

Ceci est accompli en divisant la chaîne de chiffres en 2 groupes, le groupe de droite avec 3 chiffres, le groupe de gauche avec tout ce qui reste, mais au moins un chiffre. Ensuite, tout est remplacé par les 2 groupes, séparés par une virgule. Cela continue jusqu'à ce que la substitution échoue. Les options "wpe" sont pour lister les erreurs, enfermer l'instruction dans une boucle avec une impression automatique et prendre l'argument suivant comme "programme" Perl (voir la commande perldoc perlrun pour plus de détails).

Meilleurs voeux ... cheers, drl

4
drl

Avec quelques implémentations awk:

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

123,456,789  

"%'"'"'d\n" est: "% (guillemet simple) (guillemet double) (guillemet simple) (guillemet double) (guillemet simple) d\n"

Cela utilisera le séparateur de milliers configuré pour vos paramètres régionaux (généralement , en anglais, espace en français, . en espagnol/allemand ...). Identique à celui retourné par locale thousands_sep

3
Ben

awk et bash ont de bonnes solutions intégrées, basées sur printf, comme décrit dans les autres réponses. Mais d'abord, sed.

Pour sed, nous devons le faire "manuellement". La règle générale est que si vous avez quatre chiffres consécutifs, suivis d'un non-chiffre (ou fin de ligne), une virgule doit être insérée entre le premier et le deuxième chiffre.

Par exemple,

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

imprimera

12345,678

Nous devons évidemment continuer à répéter le processus, afin de continuer à ajouter suffisamment de virgules.

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

Dans sed, la commande t spécifie une étiquette vers laquelle passer si le dernier s/// la commande a réussi. Je définis donc une étiquette avec :restart, pour qu'il recule.

Voici une démo bash (sur ideone ) qui fonctionne avec n'importe quel nombre de chiffres:

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
3
Aaron McDaid

Un cas d'utilisation courant consiste à modifier la sortie d'un pipeline de commandes afin que les nombres décimaux soient imprimés avec mille séparateurs. Plutôt que d'écrire une fonction ou un script, je préfère utiliser une technique que je peux personnaliser à la volée pour toute sortie d'un pipeline Unix.

J'ai trouvé que printf (fourni par Awk) était le moyen le plus flexible et le plus mémorable pour y parvenir. Le caractère apostrophe/guillemet simple est spécifié par [~ # ~] posix [~ # ~] as a modifier to format decimal et a l'avantage d'être sensible aux paramètres régionaux, de sorte qu'il n'est pas limité à l'utilisation de virgules.

Lors de l'exécution de commandes Awk à partir d'un shell Unix, il peut être difficile de saisir un caractère guillemet simple dans une chaîne délimitée par des guillemets simples (pour éviter l'expansion du shell des variables de position, par exemple, $1). Dans ce cas, je trouve que le moyen le plus lisible et le plus fiable pour saisir le caractère guillemet simple est de le saisir comme une séquence d'échappement octale (commençant par \0).

Exemple:

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

Sortie simulée d'un pipeline montrant quels répertoires utilisent le plus d'espace disque:

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

D'autres solutions sont répertoriées dans Comment échapper à un guillemet simple dans awk .

Remarque: comme mis en garde dans Print a Single Quote , il est recommandé d'éviter l'utilisation de séquences d'échappement hexadécimales car elles ne fonctionnent pas de manière fiable sur différents systèmes.

2
Anthony Geoghegan

Une solution bash/awk (comme demandé) qui fonctionne quelle que soit la longueur du numéro et utilise , Quel que soit le paramètre thousands_sep Des paramètres régionaux, et où que vous soyez les chiffres sont en entrée et évite d'ajouter le séparateur de milliers après dans 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}'

Donne:

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

Avec des implémentations de awk comme mawk qui ne prennent pas en charge les opérateurs d'expression régulière d'intervalle, changez l'expression rationnelle en /(^|[^.0123456789])[0123456789][0123456789][0123456789][0123456789]+/

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
1
user2796674
$ echo 1232323 | awk '{printf(fmt,$1)}' fmt="%'6.3f\n"
12,32,323.000
1
Akshay Hegde

Si vous regardez de gros chiffres, je n'ai pas pu faire fonctionner les solutions ci-dessus. Par exemple, permet d'obtenir un très grand nombre:

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

Remarque J'ai besoin de tr pour supprimer la sortie de nouvelle ligne antislash de bc. Ce nombre est trop grand pour être traité comme un nombre flottant ou un bit fixe dans awk, et je ne veux même pas construire une expression rationnelle suffisamment grande pour prendre en compte tous les chiffres de sed. Je peux plutôt l'inverser et mettre des virgules entre des groupes de trois chiffres, puis l'annuler:

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

1
Michael Benedict

Je voulais aussi avoir la partie après le séparateur décimal correctement séparé/espacé, j'ai donc écrit ce script sed qui utilise certaines variables Shell pour s'adapter aux préférences régionales et personnelles. Il prend également en compte différents conventions pour le nombre de chiffres regroupés :

#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
erik