Додайте в роздільник тисячі


37

В пітоні

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

Розділити число на триплети, наприклад:

 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")'
Рахул Патіл

тепер я розумію, але це здається складнимecho 123456789 | awk '$0=gensub(/(...)/,"\\1,","g"){sub(",$",""); print}'
Рахул Патіль

1
Це перше sedпрацює лише в тому випадку, якщо число становить рівно 9 цифр. printfНе працює на Zsh. Таким чином, друга sedвідповідь, мабуть, найкраща.
Патрік

1
@RahulPatil Це працює лише належним чином, якщо кількість цифр кратна 3. Спробуйте з "12345678", і ви побачите, що я маю на увазі.
Патрік

1
Ви можете це зробити echo 123456789 | awk '{printf ("%'\''d\n", $0)}'(що, очевидно, не завжди працює на Linux !?, але працює чудово на AIX та Solaris)
Йохан

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
Я на базі 4.1.2, і він не підтримує ... :(
msb

@msb Здається, це залежить від вашої системи vsnprintf. У системі GNU / Linux, схоже, glibc підтримує її щонайменше з 1995 року.
Mikel

2
Примітка printf використовує роздільник тисяч для вашого поточного локалу , який може бути комою, крапкою або взагалі нічого. Ви можете, export LC_NUMERIC="en_US"якщо хочете застосувати коси.
medmunds

Отримайте список підтримуваних локалів за допомогою locale -a. Мені довелося скористатисьen_US.utf8
нехай

7

Ви можете використовувати numfmt:

$ numfmt --grouping 123456789
123,456,789

Або:

$ numfmt --g 123456789
123,456,789

Зауважте, що numfmt не є утилітою POSIX, вона є частиною ядер GNU.


1
Дякую за пораду "групування". У другому прикладі (--g) ви мали на увазі написати щось подібне, -d, --groupingоскільки подвійні переноси потребують довгих варіантів?
Скакаючий Зайчик

--gвідмінно працює для мене замість --grouping, тобто numfmt --g 1234567890і numfmt --grouping 1234567890робити те ж саме. Це дуже корисна невелика утиліта.
матст

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 цифрами, лівою групою, що залишилася, але щонайменше однією цифрою. Потім все замінюється двома групами, розділеними комою. Це триває, поки заміна не відбудеться. Параметри "wpe" призначені для переліку помилок, укладіть оператор всередині циклу з автоматичним друком і прийміть наступний аргумент як perl "програма" (див. Команду perldoc perlrun для деталей).

Найкращі побажання ... ура, др


Дякуємо анонімним за відгуки. Навіть низовий потік може бути корисним, але лише за умови пояснення - прокоментуйте, будь ласка, те, що ви бачили, що було неправильно. Дякую ... ура
drl

Я думаю, що голосування тут є тому, що ви не пояснили, що робить команда. OP попросив BASH/ AWKальтернативи , щоб він не міг використовувати PERLраніше. У будь-якому випадку, краще пояснити, що робить команда - особливо це стосується однолінійних.
AnthonyK

@AnthonyK - дякую за ймовірне пояснення. Я додав коментарі, щоб коротко пояснити, як це працює. Я думаю, що альтернативні рішення часто корисні, але ваш пункт про те, що, можливо, не використовується 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 можуть виникнути труднощі з введенням символу котирування синглів всередині рядка, обмеженого одинарними лапками (щоб уникнути розширення оболонки позиційних змінних, наприклад, $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
З усіх наведених тут відповідей на дивовижних відповідях цей, безумовно, найбільш витончений (ІМХО). Не потрібно ламати цитату з іншими цитатами, як в інших рішеннях.
TSJNachos117

Спасибі @ TSJNachos117 Найскладніше - пам’ятати, що вісімкове кодування для символу апострофа є \047.
Ентоні Г - справедливість для Моніки

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, щоб вона стрибнула назад.

Ось демонстрація bash (на 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видалити вихідний рядок нового рядка з bc. Це число занадто велике, щоб його розцінювати як плаваюче чи фіксоване бітове число, і я навіть не хочу будувати регулярний вираз з великим розміром, щоб враховувати всі цифри в sed. Швидше, я можу повернути його назад і поставити коми між групами з трьох цифр, а потім відмінити його:

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, але у всіх випадках Awk не мав проблем із великою кількістю. Я подумав ще про це, і мені прийшло в голову, що всі системи, на яких я експериментував, були 64-розрядні (навіть дуже стара VM, що працює з непідтримуваною RHEL 5). Це не було , поки я не відчув старі коліна верхом , що працює під управлінням 32-розрядну ОС , що я був в змозі повторити своє питання: awk: run time error: improper conversion(number 1) in printf("%'d.
Ентоні Г - справедливість для Моніки

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éphaneChazelas: Ви можете взяти висновок цієї останньої команди rev і передати її 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

Рішення 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, які не підтримують операторів інтервальних регулярних виразів, змініть регулярний вираз/(^|[^.0123456789])[0123456789][0123456789][0123456789][0123456789]+/

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.