Bir numaraya binlerce ayırıcı ekleyin


36

Python'da

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

Bir sayıyı üçüzlere bölmek için, örneğin:

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

Bash / awk ile aynı nasıl yapılır?

Yanıtlar:


29

İle sed:

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

(Bunun yalnızca tam 9 rakam için çalıştığını unutmayın!)

veya bununla sed:

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

İle printf:

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

Ayrıca awk ile çalışıyorum ama en sonunda virgül ekleyinecho 123456789 | awk '$0=gensub(/(...)/,"\\1,","g")'
Rahul Patil

şimdi anlıyorum ama bu çok karmaşık görünüyorecho 123456789 | awk '$0=gensub(/(...)/,"\\1,","g"){sub(",$",""); print}'
Rahul Patil

1
Bu ilk önce sedsadece rakam tam olarak 9 rakam ise işe yarar. printfZsh üzerinde çalışmaz. Böylece ikinci sedcevap muhtemelen en iyisidir.
Patrick,

1
@RahulPatil Yalnızca rakam sayısı 3'ün katıysa, düzgün çalışır. "12345678" ile deneyin ve ne demek istediğimi göreceksiniz.
Patrick

1
Yapabilirsiniz echo 123456789 | awk '{printf ("%'\''d\n", $0)}'(açıkça Linux'ta çalışmaz!?, Ama AIX ve Solaris'te iyi çalışır)
Johan

51

bashbireyin printfdestekler hemen hemen her şeyi yapabilirsiniz printfC fonksiyonu

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

printf coreutils den aynı şeyi yapacak

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

Bu şimdi zshde, burada güncellenen yazı olarak desteklenmektedir .
don_crissti

1
Ben 4.1.2 bash ve desteklemiyor ... :(
msb

@ msb Sisteminizin bağlı görünüyor vsnprintf. Bir GNU / Linux sisteminde, glibc, en azından 1995'ten beri bunu desteklemiş görünüyor.
Mikel

2
Printf , geçerli yerel ayarınız için binlik ayırıcısını kullanır; bu virgül, nokta veya hiç bir şey olmayabilir. export LC_NUMERIC="en_US"Virgül zorlamak istersen yapabilirsin .
medmunds

İle desteklenen yerel ayarların listesini alın locale -a. en_US.utf8
Kullanmak

7

Numfmt kullanabilirsiniz:

$ numfmt --grouping 123456789
123,456,789

Veya:

$ numfmt --g 123456789
123,456,789

Numfmt'nin bir POSIX yardımcı programı olmadığını, GNU coreutils'in bir parçası olduğunu unutmayın.


1
"Gruplandırma" ipucu için teşekkürler. İkinci örnekte (--g), -d, --groupingçift ​​tirelemelerin uzun seçeneklere ihtiyaç duymasından dolayı gibi bir şey mi yazdınız ?
Atlamalı Tavşan

--gyerine benim için para cezası çalışır --grouping, yani numfmt --g 1234567890ve numfmt --grouping 1234567890aynı şeyi yapın. Çok kullanışlı bir yardımcı program.
mattst,

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

üretir:

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

Bu, rakam dizesini 2 gruba, sağ gruba 3 hane, sol gruba kalanı sol gruba ayırarak gerçekleştirilir, ancak en az bir hane. Sonra her şey virgülle ayrılmış 2 grupla değiştirilir. Bu, ikame başarısız olana kadar devam eder. "Wpe" seçenekleri hata listeleme içindir, ifadeyi otomatik yazdırmalı bir döngü içine alın ve bir sonraki argümanı perl "program" olarak alın (ayrıntılar için perldoc perlrun komutuna bakın).

En iyi dileklerimle ... şerefe, drl


Geri bildiriminiz için anonim olarak teşekkür ederiz. Olumsuz bir oy bile faydalı olabilir, ancak yalnızca açıklandığı takdirde - lütfen yanlış olduğunu gördükleriniz hakkında yorum yapın. Teşekkürler ... Şerefe
Drl

Bence buradaki olumsuzluk, komutun ne yaptığını açıklamadığınızdan dolayı. OP bir istedi BASH/ AWKKullandığı olmayabilir bu yüzden alternatif PERLönce. Her durumda, komutun ne yaptığını açıklamak en iyisi - özellikle tek gömlekler için.
AnthonyK

@AnthonyK - olası açıklama için teşekkürler. Nasıl çalıştığını kısaca açıklamak için yorumlar ekledim. Alternatif çözümlerin çoğu zaman yararlı olduğunu düşünüyorum, ancak muhtemelen perl kullanmamış
olmanız

Bu sayfada sed ve python önerilerini denedim. Perl betiği bütün bir dosya için çalışan tek kişiydi. Dosya metin ve sayılarla dosyalandı.
Mark

3

Bazı awkuygulamalarda:

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

123,456,789  

"%'"'"'d\n"şudur: "%(tekli teklif) (çiftli teklif) (tekli teklif) (çiftli teklif) (tekli teklif) d \ n"

Bu, yerel ayarlarınız için yapılandırılmış bin ayırıcıyı kullanacaktır (genellikle ,İngilizce yerel yerlerde, Fransızca'da, .İspanyolca'da / Almanca'da ...). Tarafından döndürülenle aynılocale thousands_sep


2

Benim için yaygın bir kullanım örneği, bir komut satırının çıktısını, ondalık sayıları bin ayracı ile yazdırılacak şekilde değiştirmek. Bir işlev veya komut dosyası yazmak yerine , bir Unix boru hattından gelen herhangi bir çıktı için anında özelleştirebileceğim bir teknik kullanmayı tercih ederim .

Bunu printfyapmanın en esnek ve akılda kalıcı yolu olarak (Awk tarafından sağlanan) buldum . Kesme işareti / tek tırnak karakteri POSIX tarafından ondalık sayıları biçimlendirmek için bir değiştirici olarak belirtilmiştir ve yerel olarak farkında olma avantajına sahiptir, bu nedenle virgül karakterleri kullanmakla sınırlı değildir.

Bir Unix kabuğundan Awk komutlarını çalıştırırken, tek tırnak işaretleri ile sınırlandırılmış bir dize içine bir alıntı karakter girme zorluğu olabilir (örneğin, konumsal değişkenlerin kabuk genişlemesini önlemek için $1). Bu durumda, tek alıntı karakterine girmenin en okunaklı ve güvenilir yolunu buluyorum, onu sekizli bir kaçış dizisi olarak girmektir (baştan \0).

Örnek:

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

Hangi dizinlerin en fazla disk alanını kullandığını gösteren bir boru hattının simüle çıkışı:

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

Diğer çözümler awk içinde tek bir alıntıdan nasıl kaçılır .

Not: Tek Bir Alıntı Basında uyarıldığı gibi , onaltılık kaçış dizilerinin farklı sistemlerde güvenilir şekilde çalışmadıklarından kaçınılması önerilir.


1
Burada listelenen tüm awk tabanlı cevapların arasında, bu kesinlikle en zarif olanıdır (IMHO). Birinin diğer çözümlerde olduğu gibi başka alıntılarla alıntı yapması gerekmez.
TSJNachos117

Thanks @ TSJNachos117 En zor kısım kesme işareti karakterinin sekizli kodlamasını hatırlamaktır \047.
Anthony G - Monica için adalet

2

awkve diğer cevaplarda açıklandığı şekilde bash, iyi yerleşik çözümler sunar printf. Ama önce, sed.

Çünkü sed"elle" yapmamız gerekiyor. Genel kural, arka arkaya dört basamağa sahipseniz, ardından basamak olmayan (veya satır sonu) ise, birinci ve ikinci basamak arasına virgül konması gerektiğidir.

Örneğin,

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

basacak

12345,678

Açıkçası, yeterince virgül eklemeye devam etmek için süreci tekrarlamaya devam etmemiz gerekiyor.

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

Olarak sed, tkomut son takdirde atladı edilecek bir etiket belirten s///komut başarılı oldu. Bu nedenle :restart, geri sıçraması için ile bir etiket tanımlarım .

İşte herhangi bir rakam ile çalışan bir bash demosu ( ideone üzerinde ):

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

Eğer BÜYÜK rakamlara bakıyorsanız yukarıdaki çözümleri çalıştıramadım. Örneğin, gerçekten büyük bir sayı elde edelim:

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

Not trTers eğik çizgi newline çıktısını bc'den çıkarmam gerekiyor. Bu sayı awk içinde bir float veya sabit bit numarası olarak değerlendirilemeyecek kadar büyük ve sed içindeki tüm rakamları hesaba katacak kadar büyük bir regexp oluşturmak bile istemiyorum. Aksine, onu tersine çevirebilir ve üç basamaklı gruplar arasına virgül koyabilir, sonra tersine çevirebilirim:

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
İyi cevap. Ancak, Awk ile büyük sayıları kullanırken bir sorunla hiç karşılaşmadım. Örneğinizi bir dizi Red Hat ve Debian tabanlı dağıtım üzerinde denedim, ancak her durumda, Awk'in çok sayıda sorunu yoktu. Bunun hakkında biraz daha düşündüm ve deneyimlediğim tüm sistemlerin 64-bit olduğunu düşündüm (desteklenmeyen RHEL 5 çalıştıran çok eski bir VM bile). Ben sorunu çoğaltmak mümkün olduğunu 32 bit işletim sistemi çalıştıran eski bir lap-top test kadar değildi: awk: run time error: improper conversion(number 1) in printf("%'d.
Anthony G - 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


@ StéphaneChazelas: Bu son rev komutunun çıktısını alabilir ve onu boruya aktarabilirsiniz sed 's/^,//g'.
TSJNachos117

0

Ayrıca bir kısım olsun istedim sonra ondalık ayırıcı doğru / ayrılmış aralıklı, bu nedenle bölgesel ve kişisel tercihlerine ayarlamak için bazı kabuk değişkenlerini kullanan bu sed-senaryoyu yazdım. Ayrıca, birlikte gruplandırılmış hane sayısı için farklı kuralları da dikkate alır :

#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

Sayının uzunluğundan bağımsız olarak çalışan ve yerel ayarın ne olduğuna bakılmaksızın çalışan ve sayıların girildiği her yerde çalışan ve aşağıdakilerden sonra bin ayırıcıyı eklemekten kaçınan A bash/ awk(istendiği gibi) çözümü :,thousands_sep1.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}'

verir:

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

Düzenli regex operatörlerini desteklemeyen awkgibi uygulamalarla mawkregexp'yi değiştirin./(^|[^.0123456789])[0123456789][0123456789][0123456789][0123456789]+/

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.