Ajouter des milliers séparateur dans un nombre

37

En python

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

Pour diviser un nombre par des triplets, par exemple:

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

Comment faire la même chose avec bash / awk?

utilisateur2496
la source

Réponses:

30

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
slm
la source
J'essaie aussi avec awk mais c'est une virgule ajoutée à la finecho 123456789 | awk '$0=gensub(/(...)/,"\\1,","g")'
Rahul Patil
maintenant je comprends mais cela semble complexeecho 123456789 | awk '$0=gensub(/(...)/,"\\1,","g"){sub(",$",""); print}'
Rahul Patil
1
Ce premier sedne fonctionne que si le nombre est exactement 9 chiffres. Le printfne fonctionne pas sur zsh. Ainsi, la deuxième sedréponse est probablement la meilleure.
Patrick
1
@RahulPatil Cela ne fonctionne correctement que si le nombre de chiffres est un multiple de 3. Essayez avec "12345678" et vous verrez ce que je veux dire.
Patrick
1
Vous pouvez le faire echo 123456789 | awk '{printf ("%'\''d\n", $0)}'(ce qui ne fonctionne évidemment pas toujours sous Linux!?, Mais fonctionne parfaitement sous AIX et Solaris)
Johan
51

bashde » printfles supports à peu près tout ce que vous pouvez faire dans la printffonction C

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
Mikel
la source
Ceci est maintenant supporté zshaussi, post mis à jour ici .
don_crissti
1
Je suis sur bash 4.1.2 et il ne supporte pas ... :(
msb
@msb Cela semble dépendre de votre système vsnprintf. Sur un système GNU / Linux, glibc semble l’avoir supporté depuis au moins 1995.
Mikel
2
Remarque printf utilise le séparateur de milliers pour votre environnement local actuel , qui peut être une virgule, un point ou rien du tout. Vous pouvez export LC_NUMERIC="en_US"si vous voulez forcer les virgules.
medmunds
Obtenir la liste des paramètres régionaux pris en charge avec locale -a. Je devais utiliseren_US.utf8
eludom
7

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.

Steven Penny
la source
1
Merci pour le "regroupement" astuce. Dans le deuxième exemple (--g), aviez-vous l'intention d'écrire quelque chose du genre -d, --groupingpuisque les doubles tirets ont besoin d'options longues?
Hopping Bunny le
--gfonctionne bien pour moi au lieu de --grouping, à savoir numfmt --g 1234567890et numfmt --grouping 1234567890faire la même chose. C'est un petit utilitaire très utile.
Mattst
4
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 scindant la chaîne de chiffres en 2 groupes, le groupe de droite avec 3 chiffres, le groupe de gauche avec le 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 destinées à la liste des erreurs, renferment l'instruction dans une boucle avec une impression automatique et prennent l'argument suivant comme "programme" perl (voir la commande perldoc perlrun pour plus de détails).

Meilleurs voeux ... A bientôt, drl

drl
la source
Merci à anonyme pour les commentaires. Même un vote négatif peut être utile, mais seulement si cela est expliqué - veuillez commenter ce que vous avez vu qui ne va pas. Merci ... à la
vôtre
Je pense que le vote négatif ici est parce que vous n'avez pas expliqué ce que la commande fait. Le PO a demandé une BASH/ AWKalternative afin qu'il ne l'ait peut-être pas utilisé PERLauparavant. Dans tous les cas, il est préférable d’expliquer ce que la commande fait, en particulier pour les doublures.
AnthonyK
@AnthonyK - merci pour l'explication probable. J'ai ajouté des commentaires pour expliquer brièvement comment cela fonctionne. Je pense que les solutions alternatives sont souvent utiles, mais votre remarque sur le fait de ne pas avoir utilisé de perl a bien été notée ...
Bravo
J'ai essayé les suggestions sed et python sur cette page. Le script Perl était le seul qui fonctionnait pour tout un fichier. Le fichier a été classé avec du texte et des chiffres.
Mark
3

Avec quelques awkimplémentations:

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

123,456,789  

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

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

Ben
la source
2

Pour moi, un cas d'utilisation courant consiste à modifier la sortie d'un pipeline de commandes afin que les nombres décimaux soient imprimés avec des séparateurs de milliers. 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é printf(fourni par Awk) le moyen le plus flexible et le plus mémorable d'accomplir cela. Le caractère apostrophe / guillemet simple est spécifié par POSIX en tant que modificateur pour formater les nombres décimaux et présente l'avantage de tenir compte des 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 d'entrer un caractère entre guillemets dans une chaîne délimitée par des guillemets simples (pour éviter le développement de shell dans les variables de position, par exemple $1). Dans ce cas, le moyen le plus lisible et le plus fiable pour saisir le caractère guillemet simple est de le saisir sous forme de 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 indiquant les répertoires utilisant 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 énumérées dans Comment échapper à une citation unique dans awk .

Remarque: comme indiqué dans Imprimer un seul devis , 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.

Anthony G - justice pour Monica
la source
1
Parmi toutes les réponses basées sur awk listées ici, celle-ci est très certainement la plus gracieuse (IMHO). Il n'est pas nécessaire de pirater une citation avec d'autres citations comme dans d'autres solutions.
TSJNachos117 le
Merci @ TSJNachos117 Le plus difficile est de se rappeler que le codage octal du caractère apostrophe est \047.
Anthony G - justice pour Monica le
2

awket bashavoir 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 tcommande spécifie une étiquette à laquelle accéder si la dernière s///commande a réussi. Je définis donc une étiquette avec :restart, afin qu’elle revienne en arrière.

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

Si vous regardez de grands nombres, je n’ai pas pu appliquer les solutions ci-dessus. Par exemple, obtenons un très grand nombre:

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

Notez que j’ai besoin trde supprimer la barre oblique inverse de la sortie ligne à barre oblique inverse. Ce nombre est trop gros pour être traité comme un nombre à virgule flottante ou fixe dans awk, et je ne souhaite même pas créer une expression rationnelle suffisamment grande pour prendre en compte tous les chiffres de sed. Je peux plutôt l'inverser et insérer 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

Michael Benedict
la source
2
Bonne réponse. Cependant, je n'ai jamais rencontré de problème d'utilisation de grands nombres avec Awk. J'ai essayé votre exemple sur un certain nombre de distributions Red Hat et Debian mais dans tous les cas, Awk n'avait aucun problème avec le nombre élevé. J'y ai réfléchi un peu plus et il m'est apparu que tous les systèmes sur lesquels j'avais fait l'expérience étaient en 64 bits (même une très vieille machine virtuelle exécutant RHEL 5 non pris en charge). Ce ne fut que je l' ai testé un vieux lap-top exécutant un système d' exploitation 32 bits que je suis en mesure de reproduire votre problème: awk: run time error: improper conversion(number 1) in printf("%'d.
Anthony G - justice pour Monica
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
utilisateur2796674
la source
Cela ajoute une fausse virgule au début si le nombre de chiffres dans le nombre est un multiple de 3.
Stéphane Chazelas
@ StéphaneChazelas: Vous pouvez prendre le résultat de la dernière commande rev et le diriger vers sed 's/^,//g'.
TSJNachos117 le
0

Je souhaitais également que la partie après le séparateur décimal soit correctement séparée / espacée. J'ai donc écrit ce script sed qui utilise certaines variables du shell pour s’adapter aux préférences régionales et personnelles. Il prend également en compte différentes 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'
}
erik
la source
0

Une solution bash/ awk(à la demande) qui fonctionne quelle que soit la longueur du nombre et utilise ,quels que soient les paramètres régionaux thousands_sep, et où que les nombres soient en entrée, évitant d’ajouter le séparateur de milliers après 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 de awktelles implémentations mawkne supportant pas les opérateurs d'intervalle regex, changez l'expression rationnelle en/(^|[^.0123456789])[0123456789][0123456789][0123456789][0123456789]+/

Stéphane Chazelas
la source