Fügen Sie Tausender-Trennzeichen in eine Zahl ein


36

In Python

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

Um eine Zahl durch Drillinge zu teilen, zB:

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

Wie mache ich dasselbe mit bash / awk?

Antworten:


29

Mit sed:

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

(Beachten Sie, dass dies nur für genau 9 Stellen funktioniert!)

oder dies mit sed:

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

Mit printf:

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

Ich versuche es auch mit awk, aber es ist am letzten Kommaecho 123456789 | awk '$0=gensub(/(...)/,"\\1,","g")'
Rahul Patil

Jetzt echo 123456789 | awk '$0=gensub(/(...)/,"\\1,","g"){sub(",$",""); print}'
Rahul Patil

1
Das sedfunktioniert erstens nur, wenn die Nummer genau 9 Stellen hat. Das printffunktioniert nicht auf zsh. Somit ist die zweite sedAntwort wahrscheinlich die beste.
Patrick

1
@ RahulPatil Das funktioniert nur richtig, wenn die Anzahl der Stellen ein Vielfaches von 3 ist. Versuchen Sie es mit "12345678" und Sie werden sehen, was ich meine.
Patrick

1
Sie können echo 123456789 | awk '{printf ("%'\''d\n", $0)}'(was offensichtlich nicht immer unter Linux funktioniert!?, Aber unter AIX und Solaris funktioniert es einwandfrei)
Johan

51

bash‚s printfunterstützt so ziemlich alles , was Sie können in der tun printfC - Funktion

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

printf von Coreutils wird das gleiche tun

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

Dies wird nun auch in dem hierzsh aktualisierten Beitrag unterstützt .
don_crissti

1
Ich bin auf Bash 4.1.2 und es unterstützt nicht ... :(
msb

@msb Es scheint von Ihrem System abzuhängen vsnprintf. Auf einem GNU / Linux-System scheint glibc es seit mindestens 1995 unterstützt zu haben.
Mikel

2
Hinweis printf verwendet das Tausendertrennzeichen für Ihr aktuelles Gebietsschema , bei dem es sich möglicherweise um ein Komma, einen Punkt oder gar nichts handelt. Sie können, export LC_NUMERIC="en_US"wenn Sie Kommas erzwingen möchten.
Medmunds

Liste der unterstützten Gebietsschemas mit abrufen locale -a. Ich mussteen_US.utf8
eludom

7

Sie können numfmt verwenden:

$ numfmt --grouping 123456789
123,456,789

Oder:

$ numfmt --g 123456789
123,456,789

Beachten Sie, dass numfmt kein POSIX-Dienstprogramm ist, sondern Teil von GNU coreutils.


1
Vielen Dank für den Tipp "Gruppierung". Wollten Sie im zweiten Beispiel (--g) so etwas schreiben, -d, --groupingda doppelte Silbentrennungen lange Optionen erfordern?
Hopping Bunny

--gfunktioniert gut für mich statt --grouping, dh numfmt --g 1234567890und numfmt --grouping 1234567890mache das gleiche. Es ist ein sehr nützliches kleines Hilfsprogramm.
13.

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

produziert:

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

Dies wird erreicht, indem die Ziffernfolge in 2 Gruppen aufgeteilt wird, die rechte Gruppe mit 3 Ziffern, die linke Gruppe mit allen verbleibenden Ziffern, aber mindestens einer Ziffer. Dann wird alles durch die 2 durch Komma getrennten Gruppen ersetzt. Dies wird fortgesetzt, bis die Substitution fehlschlägt. Die Optionen "wpe" dienen zur Fehlerauflistung, schließen die Anweisung in einer Schleife mit einem automatischen Ausdruck ein und nehmen das nächste Argument als Perl "program" (Details siehe Befehl perldoc perlrun).

Beste Wünsche ... Prost, drl


Vielen Dank an anonym für das Feedback. Sogar ein Downvote kann nützlich sein, aber nur wenn es erklärt wird - kommentieren Sie bitte, was Sie als falsch angesehen haben. Danke ...
Prost

Ich denke, die Ablehnung hier ist, weil Sie nicht erklärt haben, was der Befehl tut. Das OP hat nach einer BASH/ AWKAlternative gefragt , die er möglicherweise noch nicht verwendet PERLhat. In jedem Fall ist es am besten zu erklären, was der Befehl bewirkt - insbesondere für Einzeiler.
AnthonyK

@AnthonyK - danke für die wahrscheinliche Erklärung. Ich habe Kommentare hinzugefügt, um kurz zu erklären, wie es funktioniert. Ich denke, alternative Lösungen sind oft nützlich, aber Ihr Punkt, dass Sie möglicherweise kein Perl verwendet haben, ist vermerkt ... Prost
Prost

Ich habe die Sed- und Python-Vorschläge auf dieser Seite ausprobiert. Das Perl-Skript war das einzige, das für eine ganze Datei funktioniert hat. Die Datei wurde mit Text und Zahlen abgelegt.
Mark

3

Mit einigen awkImplementierungen:

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

123,456,789  

"%'"'"'d\n"ist: "%(einfaches Anführungszeichen) (doppeltes Anführungszeichen) (einfaches Anführungszeichen) (doppeltes Anführungszeichen) d \ n"

Dabei wird das konfigurierte Tausendertrennzeichen für Ihr Gebietsschema verwendet (normalerweise ,in englischer Sprache, Leerzeichen in Französisch, .in Spanisch / Deutsch ...). Gleich wie von zurückgegebenlocale thousands_sep


2

Ein häufiger Anwendungsfall für mich ist es, die Ausgabe einer Befehlspipeline so zu ändern, dass Dezimalzahlen mit Tausendertrennzeichen gedruckt werden. Anstatt eine Funktion oder ein Skript zu schreiben, bevorzuge ich eine Technik, die ich spontan für jede Ausgabe aus einer Unix-Pipeline anpassen kann .

Ich habe festgestellt printf(bereitgestellt von Awk), dass dies der flexibelste und einprägsamste Weg ist, dies zu erreichen. Das Apostroph- / Anführungszeichen wird von POSIX angegeben als Modifikator zum Formatieren von Dezimalzahlen angegeben und hat den Vorteil, dass es das Gebietsschema berücksichtigt und nicht auf die Verwendung von Kommazeichen beschränkt ist.

Wenn Sie Awk-Befehle in einer Unix-Shell ausführen, kann es schwierig sein, ein einfaches Anführungszeichen in eine durch einfache Anführungszeichen begrenzte Zeichenfolge einzugeben (um die Shell-Erweiterung von Positionsvariablen zu vermeiden, z $1. B. ). In diesem Fall finde ich die lesbarste und zuverlässigste Möglichkeit, ein einfaches Anführungszeichen einzugeben, darin, es als oktale Escape-Sequenz (beginnend mit \0) einzugeben .

Beispiel:

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

Simulierte Ausgabe einer Pipeline, aus der hervorgeht, welche Verzeichnisse den meisten Speicherplatz belegen:

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

Andere Lösungen sind in aufgeführt So vermeiden Sie ein einfaches Anführungszeichen in awk .

Hinweis: wie gegen warnte in Apostroph drucken , es wird empfohlen , die Verwendung von hexadezimalen Escape - Sequenzen zu vermeiden , da sie über verschiedene Systeme nicht zuverlässig arbeiten.


1
Von allen hier aufgeführten awk-basierten Antworten ist diese mit Sicherheit die anmutigste (IMHO). Man muss kein Zitat mit anderen Zitaten hacken, wie es bei anderen Lösungen der Fall ist.
TSJNachos117

Thanks @ TSJNachos117 Der schwierigste Teil ist, sich daran zu erinnern, dass die Oktalcodierung für das Apostrophzeichen lautet \047.
Anthony G - Gerechtigkeit für Monica

2

awkund bashhaben gute integrierte Lösungen, basierend aufprintf , wie in den anderen Antworten beschrieben. Aber zuerst sed.

Zum sed müssen wir es "manuell" tun. Die allgemeine Regel lautet: Wenn Sie vier aufeinanderfolgende Ziffern gefolgt von einer Nicht-Ziffer (oder einem Zeilenende) haben, sollte ein Komma zwischen der ersten und der zweiten Ziffer eingefügt werden.

Beispielsweise,

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

wird gedruckt

12345,678

Wir müssen dann natürlich den Vorgang wiederholen, um immer genug Kommas hinzuzufügen.

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

In sedgibt der tBefehl eine Bezeichnung an, zu der gesprungen wird, wenn der letzte s///Befehl erfolgreich war. Ich definiere daher ein Label mit :restart, damit es zurückspringt.

Hier ist eine Bash-Demo (auf Ideone ), die mit einer beliebigen Anzahl von Ziffern funktioniert:

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

Wenn Sie sich GROSSE Zahlen ansehen, konnte ich die obigen Lösungen nicht zum Laufen bringen. Lassen Sie uns zum Beispiel eine wirklich große Zahl erhalten:

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

Hinweis Ich benötige das tr, um die Ausgabe von Backslash Newline von bc zu entfernen. Diese Zahl ist zu groß, um sie in awk als Float- oder feste Bitnummer zu behandeln, und ich möchte nicht einmal einen regulären Ausdruck erstellen, der groß genug ist, um alle Ziffern in sed zu berücksichtigen. Eher kann ich es umkehren und Kommas zwischen Gruppen von drei Ziffern setzen, dann es wieder rückgängig machen:

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
Gute Antwort. Ich bin jedoch noch nie auf ein Problem gestoßen, wenn ich mit Awk große Zahlen verwendet habe. Ich habe Ihr Beispiel für eine Reihe von Red Hat- und Debian-basierten Distributionen ausprobiert, aber in allen Fällen hatte Awk kein Problem mit der großen Anzahl. Ich dachte noch etwas darüber nach und stellte fest, dass alle Systeme, auf denen ich experimentiert hatte, 64-Bit waren (sogar eine sehr alte VM, auf der RHEL 5 nicht unterstützt wird). Es war nicht , bis ich einen alten Laptop-getestet , um eine 32-Bit - Betriebssystem ausgeführt wird, dass ich in der Lage war , das Problem zu reproduzieren: awk: run time error: improper conversion(number 1) in printf("%'d.
Anthony G - Gerechtigkeit für 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

Das fügt ein falsches Komma hinzu, wenn die Anzahl der Stellen in der Zahl ein Vielfaches von 3 ist.
Stéphane Chazelas

@ StéphaneChazelas: Sie können die Ausgabe dieses letzten rev-Befehls übernehmen und an weiterleiten sed 's/^,//g'.
TSJNachos117

0

Ich wollte auch, dass der Teil nach dem Dezimaltrennzeichen richtig getrennt / beabstandet ist, deshalb habe ich dieses sed-Skript geschrieben, das einige Shell-Variablen verwendet, um regionale und persönliche Präferenzen anzupassen. Dabei werden auch unterschiedliche Konventionen für die Anzahl der zusammen gruppierten Ziffern berücksichtigt :

#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

A bash/ awk(nach Wunsch) Lösung , die unabhängig von der Länge der Anzahl und Anwendungen arbeitet ,unabhängig von der Gebietsschema - thousands_sepEinstellung, und überall dort , wo die Zahlen sind im Ein- und vermeidet Zugabe des Tausendertrennzeichen nach in 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}'

Gibt:

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

Mit awkImplementierungen wie mawkdas nicht die Intervall regex Operatoren unterstützen, ändern Sie den regulären Ausdruck/(^|[^.0123456789])[0123456789][0123456789][0123456789][0123456789]+/

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.