เพิ่มตัวคั่นหลักพันในตัวเลข


36

ในหลาม

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

หากต้องการหารตัวเลขด้วย triplets เช่น:

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

ทำอย่างไรกับ bash / awk

คำตอบ:


29

ด้วยsed:

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

(โปรดทราบว่าวิธีนี้ใช้งานได้เพียง 9 หลักเท่านั้น!)

หรือสิ่งนี้ด้วยsed:

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

ด้วยprintf:

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

ฉันยังลองด้วย awk แต่เพิ่มคอมม่าในที่สุดecho 123456789 | awk '$0=gensub(/(...)/,"\\1,","g")'
Rahul Patil

ตอนนี้ฉันเข้าใจแล้ว แต่ดูเหมือนว่าซับซ้อนecho 123456789 | awk '$0=gensub(/(...)/,"\\1,","g"){sub(",$",""); print}'
ราหุลปาติล

1
สิ่งแรกนั้นsedจะทำงานได้ก็ต่อเมื่อตัวเลขนั้นเป็น 9 หลัก printfไม่ทำงานบน zsh ดังนั้นsedคำตอบที่สองน่าจะดีที่สุด
Patrick

1
@RahulPatil ใช้งานได้ดีถ้าจำนวนหลักเป็นจำนวนเท่าของ 3 ลองด้วย "12345678" แล้วคุณจะเห็นว่าฉันหมายถึงอะไร
Patrick

1
คุณสามารถทำได้echo 123456789 | awk '{printf ("%'\''d\n", $0)}'(ซึ่งเห็นได้ชัดว่าไม่สามารถใช้งานได้กับ Linux!? แต่ทำงานได้ดีบน AIX และ Solaris)
Johan

51

bash's printfสนับสนุนทุกอย่างสวยมากที่คุณสามารถทำได้ในprintfฟังก์ชัน C

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

printf จาก coreutils จะทำเช่นเดียวกัน

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

นี้ได้รับการสนับสนุนในขณะนี้zshด้วยการปรับปรุงการโพสต์ที่นี่
don_crissti

1
ฉันใช้ bash 4.1.2 และไม่รองรับ ... :(
msb

@msb ดูเหมือนว่าจะขึ้นอยู่กับระบบของvsnprintfคุณ บนระบบ GNU / Linux glibc ดูเหมือนจะรองรับตั้งแต่อย่างน้อย 1995
Mikel

2
Note printf ใช้ตัวคั่นหลักพันสำหรับตำแหน่งที่ตั้งปัจจุบันของคุณซึ่งอาจเป็นเครื่องหมายจุลภาคจุดหรือไม่มีอะไรเลย คุณสามารถexport LC_NUMERIC="en_US"ถ้าคุณต้องการบังคับให้จุลภาค
medmunds

locale -aได้รับรายชื่อของสถานที่ได้รับการสนับสนุนด้วย ฉันต้องใช้en_US.utf8
eludom

7

คุณสามารถใช้ numfmt:

$ numfmt --grouping 123456789
123,456,789

หรือ:

$ numfmt --g 123456789
123,456,789

โปรดทราบว่า numfmt ไม่ใช่ยูทิลิตี้ POSIX มันเป็นส่วนหนึ่งของ GNU coreutils


1
ขอบคุณสำหรับเคล็ดลับ "การจัดกลุ่ม" ในตัวอย่างที่สอง (--g) คุณหมายถึงเขียนอะไรเช่นนี้-d, --groupingเนื่องจากการใส่ยัติภังค์คู่ต้องการตัวเลือกยาว ๆ ?
Hopping Bunny

--gทำงานได้ดีสำหรับฉันแทน--groupingคือnumfmt --g 1234567890และnumfmt --grouping 1234567890ทำสิ่งเดียวกัน มันเป็นประโยชน์เล็ก ๆ น้อย ๆ ที่มีประโยชน์มาก
mattst

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

ผลิต:

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 กลุ่มกลุ่มทางขวามือด้วยตัวเลข 3 หลักกลุ่มทางซ้ายมือที่มีสิ่งใดเหลืออยู่ แต่อย่างน้อยหนึ่งหลัก จากนั้นทุกอย่างจะถูกแทนที่ด้วย 2 กลุ่มคั่นด้วยเครื่องหมายจุลภาค สิ่งนี้จะดำเนินต่อไปจนกว่าการทดแทนจะล้มเหลว ตัวเลือก "wpe" ใช้สำหรับการแสดงรายการข้อผิดพลาดล้อมรอบคำสั่งภายในลูปที่มีการพิมพ์อัตโนมัติและรับอาร์กิวเมนต์ถัดไปเป็น "โปรแกรม" perl (ดูคำสั่ง perldoc perlrun สำหรับรายละเอียด)

ด้วยความปรารถนาดี ... ไชโย drl


ขอบคุณที่ไม่ระบุชื่อสำหรับข้อเสนอแนะ แม้แต่การลงคะแนนเสียงก็มีประโยชน์ แต่ถ้าได้รับการอธิบาย - โปรดแสดงความคิดเห็นในสิ่งที่คุณเห็นว่าผิด ขอบคุณ ... ไชโย
drl

ฉันคิดว่า downvote ที่นี่เป็นเพราะคุณไม่ได้อธิบายว่าคำสั่งทำอะไร OP ขอBASH/ a AWKทางเลือกดังนั้นเขาอาจไม่เคยใช้มาPERLก่อน ในกรณีใด ๆ ที่ดีที่สุดที่จะอธิบายสิ่งที่คำสั่ง - โดยเฉพาะอย่างยิ่งสำหรับหนึ่งสมุทร
AnthonyK

@AththonyK - ขอบคุณสำหรับคำอธิบายที่น่าจะเป็นไปได้ ฉันเพิ่มความคิดเห็นเพื่ออธิบายสั้น ๆ ว่ามันทำงานอย่างไร ฉันคิดว่าวิธีการแก้ปัญหาทางเลือกมักจะมีประโยชน์ แต่จุดของคุณเกี่ยวกับอาจไม่ได้ใช้ perl ถูกบันทึกไว้ ... ไชโย
drl

ฉันลองแนะนำ sed และ python ในหน้านี้ สคริปต์ Perl เป็นหนึ่งเดียวที่ทำงานสำหรับไฟล์ทั้งหมด ไฟล์ถูกยื่นด้วยข้อความและตัวเลข
ทำเครื่องหมาย

3

ด้วยawkการใช้งานบางอย่าง:

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

123,456,789  

"%'"'"'d\n"คือ: "%(เครื่องหมายคำพูดเดี่ยว) (เครื่องหมายคำพูดคู่) (เครื่องหมายคำพูดเดี่ยว) (เครื่องหมายคำพูดคู่) (เครื่องหมายคำพูดเดี่ยว) d \ n"

ที่จะใช้ตัวคั่นหลักพันที่กำหนดค่าไว้สำหรับโลแคลของคุณ (โดยทั่วไปคือโลแคล,ภาษาอังกฤษ, ช่องว่างในฝรั่งเศส, .ในสเปน / เยอรมัน ... ) เหมือนกับที่ส่งคืนโดยlocale thousands_sep


2

กรณีการใช้งานทั่วไปสำหรับฉันคือการปรับเปลี่ยนผลลัพธ์ของไพพ์ไลน์คำสั่งเพื่อให้พิมพ์เลขทศนิยมด้วยตัวคั่นหลักพัน แทนที่จะเขียนฟังก์ชั่นหรือสคริปต์ฉันชอบที่จะใช้เทคนิคที่ฉันสามารถปรับแต่งได้ทันทีสำหรับเอาต์พุตใด ๆจากไปป์ไลน์ Unix

ฉันได้พบprintf(ให้โดย Awk) ว่าเป็นวิธีที่ยืดหยุ่นและน่าจดจำที่สุดในการทำสิ่งนี้ให้สำเร็จ อักขระเครื่องหมายอัญประกาศเดี่ยว / อัญประกาศเดี่ยวถูกระบุโดยPOSIXเป็นตัวดัดแปลงเพื่อจัดรูปแบบตัวเลขทศนิยมและมีข้อได้เปรียบที่ทราบถึงโลแคลดังนั้นจึงไม่ จำกัด การใช้อักขระเครื่องหมายจุลภาค

เมื่อรันคำสั่ง Awk จาก Unix shell อาจมีปัญหาในการป้อนอักขระ singe-quote ภายในสตริงที่คั่นด้วยเครื่องหมายคำพูดเดี่ยว (เพื่อหลีกเลี่ยงการขยายเชลล์ของตัวแปรตำแหน่งเช่น$1) ในกรณีนี้ฉันพบว่าวิธีที่ง่ายที่สุดและเชื่อถือได้ในการป้อนตัวอักษรคำพูดเดียวคือการใส่มันเป็นลำดับเลขฐานแปด (เริ่มต้นด้วย\0)

ตัวอย่าง:

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

เอาต์พุตจำลองของไพพ์ไลน์แสดงว่าไดเร็กทอรีใดกำลังใช้พื้นที่ดิสก์มากที่สุด:

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

โซลูชั่นอื่น ๆ ที่มีการระบุไว้ในวิธีการหลบหนีคำพูดเดียวภายใน awk

หมายเหตุ: ตามที่ได้รับคำเตือนในการพิมพ์คำพูดเดียวขอแนะนำให้หลีกเลี่ยงการใช้ลำดับการหลบหลีกเลขฐานสิบหกเนื่องจากไม่สามารถทำงานได้อย่างน่าเชื่อถือในระบบที่แตกต่างกัน


1
จากคำตอบที่ใช้ awk ทั้งหมดที่ระบุไว้ที่นี่คำตอบนี้เป็นคำที่งดงามที่สุด (IMHO) เราไม่จำเป็นต้องแฮ็กใบเสนอราคาพร้อมราคาอื่น ๆ เหมือนในโซลูชันอื่น ๆ
TSJNachos117

ขอบคุณ @ TSJNachos117 \047ส่วนที่ยากที่สุดคือการจดจำว่าการเข้ารหัสฐานแปดตัวอักษรวรรคคือ
Anthony G - ความยุติธรรมสำหรับโมนิก้า

2

awkและbashมีวิธีแก้ปัญหาในตัวที่ดีตามที่printfอธิบายไว้ในคำตอบอื่น ๆ แต่ก่อนsedอื่น

สำหรับsedเราต้องทำด้วยตนเอง "ด้วยตนเอง" กฎทั่วไปคือถ้าคุณมีตัวเลขสี่หลักติดต่อกันตามด้วยไม่ใช่ตัวเลข (หรือจุดสิ้นสุดของบรรทัด) ดังนั้นควรใส่เครื่องหมายจุลภาคระหว่างหลักแรกและตัวที่สอง

ตัวอย่างเช่น,

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

จะพิมพ์

12345,678

เห็นได้ชัดว่าเราจำเป็นต้องทำซ้ำกระบวนการเพื่อเพิ่มจุลภาคมากพอ

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

ในsedการtสั่งระบุฉลากที่จะเพิ่มขึ้นถึงถ้าที่ผ่านมาs///คำสั่งก็ประสบความสำเร็จ ฉันจึงกำหนดป้ายกำกับด้วย:restartเพื่อที่จะข้ามไป

นี่คือการสาธิตทุบตี (บนideone ) ที่ทำงานกับตัวเลขจำนวนใด ๆ :

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

หากคุณกำลังดูตัวเลขขนาดใหญ่ฉันไม่สามารถแก้ไขปัญหาข้างต้นได้ ตัวอย่างเช่นลองรับจำนวนมากจริงๆ:

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

หมายเหตุฉันต้องการที่trจะลบออก backslash ขึ้นบรรทัดใหม่ออกจาก bc หมายเลขนี้ใหญ่เกินไปที่จะถือเป็นเลขทศนิยมหรือเลขคงที่ใน awk และฉันไม่ต้องการสร้าง regexp ที่ใหญ่พอที่จะรองรับตัวเลขทั้งหมดในตัวเลข แต่ฉันสามารถย้อนกลับและใส่เครื่องหมายจุลภาคระหว่างกลุ่มตัวเลขสามหลักแล้วกลับมาอีกครั้ง:

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
คำตอบที่ดี. อย่างไรก็ตามฉันไม่เคยพบปัญหาในการใช้ Awk เป็นจำนวนมาก ฉันลองตัวอย่างของคุณเกี่ยวกับ Red Hat และ Debian-based ดิสทริบิวชัน แต่ในทุกกรณี Awk ไม่มีปัญหากับจำนวนมาก ฉันคิดเพิ่มเติมเกี่ยวกับเรื่องนี้และมันเกิดขึ้นกับฉันว่าระบบทั้งหมดที่ฉันได้ทดลองใช้นั้นเป็น 64 บิต (แม้แต่ VM ที่เก่ามากที่ใช้ RHEL 5 ที่ไม่รองรับ) มันไม่ได้จนกว่าผมทดสอบตักบนเก่าเรียกใช้ระบบปฏิบัติการ 32 awk: run time error: improper conversion(number 1) in printf("%'dบิตที่ผมสามารถที่จะทำซ้ำปัญหาของคุณ:
Anthony G - ความยุติธรรมสำหรับโมนิก้า

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

ที่เพิ่มเครื่องหมายจุลภาคนำปลอมถ้าจำนวนตัวเลขในจำนวนนั้นเป็น 3 ตัว
Stéphane Chazelas

@ StéphaneChazelas: sed 's/^,//g'คุณอาจจะใช้การส่งออกของคำสั่งที่รอบที่ผ่านมาและท่อมัน
TSJNachos117

0

ฉันยังอยากจะมีส่วนหลังจากคั่นทศนิยมอย่างถูกต้องแยกออกจากกัน / ระยะห่างดังนั้นผมเขียนนี้ sed สคริปต์ที่ใช้ตัวแปรเปลือกบางอย่างเพื่อปรับการตั้งค่าในระดับภูมิภาคและส่วนบุคคล นอกจากนี้ยังคำนึงถึงอนุสัญญาที่แตกต่างกันสำหรับจำนวนตัวเลขที่จัดกลุ่มเข้าด้วยกัน :

#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(ตามที่ร้องขอ) ที่ทำงานโดยไม่คำนึงถึงความยาวของตัวเลขและการใช้งาน,โดยไม่คำนึงถึงการthousands_sepตั้งค่าของโลแคลและที่ใดก็ตามที่ตัวเลขอยู่ในอินพุตและหลีกเลี่ยงการเพิ่มตัวคั่นหลักพันใน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}'

ให้:

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

ด้วยawkการใช้งานแบบmawkที่ไม่รองรับโอเปอเรเตอร์ช่วงเวลา regex เปลี่ยน regexp เป็น/(^|[^.0123456789])[0123456789][0123456789][0123456789][0123456789]+/

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.