Добавьте разделитель тысяч в число


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?

Ответы:


30

С 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
Eludom

7

Вы можете использовать numfmt:

$ numfmt --grouping 123456789
123,456,789

Или:

$ numfmt --g 123456789
123,456,789

Обратите внимание, что numfmt - это не утилита POSIX, а часть GNU coreutils.


1
Спасибо за совет по группировке. Во втором примере (--g) вы хотели написать что-то вроде, -d, --groupingтак как двойные переносы требуют длинных опций?
Прыгающий кролик

--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

Я думаю, что здесь отрицательный голос, потому что вы не объяснили, что делает команда. ОП попросил BASH/ AWKальтернативу, поэтому он, возможно, не использовал PERLраньше. В любом случае, лучше всего объяснить, что делает команда, особенно для однострочников.
AnthonyK

@AnthonyK - спасибо за вероятное объяснение. Я добавил комментарии, чтобы кратко объяснить, как это работает. Я думаю, что альтернативные решения часто бывают полезны, но ваша точка зрения о том, что возможно не использовался Perl, отмечается ... ура
drl

Я попробовал предложения sed и python на этой странице. Perl-скрипт был единственным, который работал для всего файла. Файл был заполнен текстом и цифрами.
Отметить

3

С некоторыми awkреализациями:

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

123,456,789  

"%'"'"'d\n"is: "%(одинарные кавычки) (двойные кавычки) (одинарные кавычки) (двойные кавычки) (одинарные кавычки) d \ n"

Это будет использовать сконфигурированный разделитель тысяч для вашей локали (обычно ,в английских локалях, пробел во французском, .испанском / немецком ...). То же, что возвращеноlocale thousands_sep


2

Обычный вариант использования для меня - изменить вывод командного конвейера так, чтобы десятичные числа печатались с тысячами разделителей. Вместо того, чтобы писать функцию или скрипт, я предпочитаю использовать технику, которую я могу настроить на лету для любого вывода из конвейера Unix.

Я нашел printf(предоставленный Awk) самый гибкий и запоминающийся способ для достижения этой цели. Символ апостроф / одинарная кавычка указывается POSIX как модификатор для форматирования десятичных чисел и имеет то преимущество, что он учитывает локаль, поэтому он не ограничен использованием символов запятой.

При запуске команд Awk из оболочки Unix могут возникнуть трудности при вводе символа одинарных кавычек внутри строки, разделенной одинарными кавычками (чтобы избежать расширения оболочки позиционных переменных, например, $1). В этом случае я считаю, что наиболее читаемый и надежный способ ввести символ одинарных кавычек - это ввести его в виде восьмеричной escape-последовательности (начиная с \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 .

Примечание. Как указано в разделе « Печать одинарных кавычек» , рекомендуется избегать использования шестнадцатеричных escape-последовательностей, поскольку они не работают надежно в разных системах.


1
Из всех ответов на основе awk, перечисленных здесь, этот, безусловно, самый изящный (ИМХО). Не нужно взламывать цитату с другими цитатами, как в других решениях.
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. Это число слишком велико, чтобы его можно было рассматривать как число с плавающей запятой или фиксированное число битов в awk, и я даже не хочу создавать регулярное выражение, достаточно большое, чтобы учесть все цифры в 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-битными (даже очень старая ВМ, работающая без поддержки 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

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

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.