Додавання з 'sed'


40

Я намагаюся виконати математичну операцію sed, але вона продовжує трактувати мої змінні як рядки. Вхід такого типу:

$ echo 12 | sed 's/[0-9]*/&+3/'
$ 12+3

Я б хотів мати 15 як вихід. Мені потрібно виконати операцію та замінити її математичний результат лише одним уривком, тому що я запускаю програму як демон Python, і я хочу уникати пасажів, таких як перенаправлення stdoutна файли, відкривати ці файли, виконувати операції, витягувати результат, робити заміна. Мені sedздається, найкраще виконати всіх в одному рядку.

Я намагався видавати і вхід, і вихід різними способами

$ echo 12 | sed 's/[0-9]*/int(&+3)/'
$ echo 12 | sed 's/[0-9]*/\int(&+3)/'
$ echo 12 | sed 's/[0-9]*/\int(&+3)/'

але результатом завжди було друкування другого поля.


12
Він розглядає ваші "змінні" як рядки, тому що це все, що робить sed - маніпуляція з рядками. У ньому немає поняття "ціле число".
Кевін

2
Мені дуже цікаво, чому ви хочете використовувати sedматематику
Девід Онелл

Я просто думав, що це може легко подавати змінні, не розумію, що це так складно!
Луїджі Тібурзі

Відповіді:


82

Якщо ви чесно хочете скористатися sed, то це шлях:

s/[0-9]/<&/g
s/0//g; s/1/|/g; s/2/||/g; s/3/|||/g; s/4/||||/g; s/5/|||||/g; s/6/||||||/g
s/7/|||||||/g; s/8/||||||||/g; s/9/|||||||||/g
: tens
s/|</<||||||||||/g
t tens
s/<//g
s/+//g
: minus
s/|-|/-/g
t minus
s/-$//
: back
s/||||||||||/</g
s/<\([0-9]*\)$/<0\1/
s/|||||||||/9/; s/||||||||/8/; s/|||||||/7/; s/||||||/6/; s/|||||/5/; s/||||/4/
s/|||/3/; s/||/2/; s/|/1/
s/</|/g
t back

Вхід:

1+2
100+250
100-250

Вихід:

3
350
-150

Якщо ви вирішите її прийняти, ваша місія полягає у здійсненні множення.


5
+1 за виклик, люби його! Може бути , це було б що - то для коду Golf ;-p
Tatjana Heuser

6
А деякі кажуть, що програмування - це не математика. Цей маленький самоцвіт спростовує їх усіх. Найкраще використання Бази 1.
Брюс Едігер

1
Хороший! - @Simon: Я закликаю вас , щоб реалізувати тетраци : P
AT

16
+1 Це прекрасний приклад того, що може спричинити помилкове уявлення в парі з творчістю.
rozcietrzewiacz

1
Множення потрібно робити за допомогою sed - і це також дуже добре масштабується до величезної кількості!
Toby Speight

20

sedТут не найкращий варіант, він не виконує арифметику спочатку (див. Збільшення числа про те, як ви могли це зробити, хоча). Ви можете зробити це за допомогою awk:

$ echo 12 | awk '{print $0+3}'
15

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

Ви також можете це зробити лише за допомогою bash:

$ echo $(( $(echo 12) + 3 ))

або використовуючи exprподібний спосіб.


17

Я спробував прийняти ваше завдання @Richter, ось що я зробив, використовуючи частину вашого коду:

sed 's/[0-9]/<&/g
s/0//g; s/1/|/g; s/2/||/g; s/3/|||/g; s/4/||||/g; s/5/|||||/g; s/6/||||||/g
s/7/|||||||/g; s/8/||||||||/g; s/9/|||||||||/g
: tens
s/|</<||||||||||/g
t tens
s/<//g
s/.*\*$/0/
s/^\*.*/0/
s/*|/*/
: mult
s/\(|*\)\*|/\1<\1*/ 
t mult
s/*//g
s/<//g
: back
s/||||||||||/</g
s/<\([0-9]*\)$/<0\1/
s/|||||||||/9/; s/||||||||/8/; s/|||||||/7/; s/||||||/6/; s/|||||/5/; s/||||/4/
s/|||/3/; s/||/2/; s/|/1/
s/</|/g
t back'

Вхід:

04*3
4*3
40*3
42*32
150*20
1*3
3*1
0*3
3*0

Результат: всі правильні результати


@SimonRichter сподіваюся, що вам сподобається !!
Луїджі Тібурзі

Кросс опублікував цю геніальну відповідь тут: codegolf.stackexchange.com/a/39882/11259
Digital Trauma

12

perlдозволяє створити дуже подібну конструкцію до seds ... одна різниця полягає в тому, що perlможна робити складніші речі ... sedдуже добре для простих текстових підстанцій

 echo 'a12' | perl -pe 's/([0-9]+)/($1+3)/e'  # the trailing /e means evaluate

вихід

a15

2
також можна зробити це без захоплення дужок:perl -pe 's/[0-9]+/$&+3/e'
glenn jackman


6

Я дійсно не розумію, чому надзвичайна складність прийнятої відповіді, будь-якого із наведених нижче, робить те, що ви хочете:

echo 12 | sed 's/[0-9]*/echo \$(( & + 3 ))/e'

або

echo 12 | sed 's/[0-9]*/expr & + 3/e'

Я думаю, це може зажадати GNU sed, але я не впевнений.


Це подовження гну.
Кевін

Гаразд, ви праві, але відповідь виходить за рамки, вона реалізує загальне доповнення, а не конкретне, ви можете годувати будь-які два числа, і ви отримаєте результат
Луїджі Тібурзі

@LuigiTiburzi Це досить просто узагальнити це на введення стилю "x + y":echo 12+3 | sed -r 's/([0-9]*) *\+ *([0-9]*)/expr \1 + \2/e'
Digital Trauma

5

Якщо вам обов'язково потрібно поєднувати регулярні вирази та арифметичні операції, виберіть мову, де параметр заміни регулярного виразу може бути функцією зворотного виклику.

Perl, Ruby, JavaScript та Python - це такі мови:

bash-4.2$ echo 12 | perl -pe 's/\d+/$&+3/e'
15

bash-4.2$ echo 12 | ruby -pe '$_.sub!(/\d+/){|s|s.to_i+3}'
15

bash-4.2$ echo 12 | js -e 'print(readline().replace(/\d+/,function(s){return parseInt(s)+3}))'
15

bash-4.2$ echo 12 | python -c 'import re;print re.sub("\d+",lambda s:str(int(s.group(0))+3),raw_input())'
15

1

Ще одне просте bashрішення, яке насправді працює в трубі:

 echo 12 | { read num; echo $(( num + 3)); }

1

Якщо ви змішуєтесь з деяким башизмом:

echo $(($(echo 12 | sed 's/[0-9]*/&+3/')))

Щоб витягнути число з тексту:

echo $(($(echo "foo12bar" | sed -r 's/[^0-9]*([0-9]*).*/\1+3/')))

Без sed, просто баш:

var="foo12bar"
echo $((${var//[^0-9]/}+3))

замінює кожну нецифрову ${var//[^0-9]/}і робить арифметику у подвійних круглих паренах:$((x+3))


2
Там немає башизму. $((...))було введено POSIX (башизм є $[...]). ${var//xxx/x}це кшизм, також скопійований zsh і bash. sed -rє GNUism
Stéphane Chazelas

0

Ось рішення Perl:

echo 12 | perl -wlpe '$_ += 3'
# Output:  15

Якщо ви хочете змінити перший набір цифр, що зустрічаються в рядку, ви можете використовувати:

echo I am 12 years old. | perl -wlpe 's/(\d+)/$1 + 3/e'
# Output:  I am 15 years old.

Якщо ви віддаєте перевагу змінювати всі набори цифр у рядку, ви можете використовувати /gмодифікатор, наприклад, такий:

echo They are 11, 12, and 13 years old. | perl -wlpe 's/(\d+)/$1 + 3/eg'
# Output:  They are 14, 15, and 16 years old.

0

Хоча використання експресії sed є великим, але воно має свої обмеження. Наприклад, такі помилки:

$ echo "1000000000000000000000000000000+1" | sed -e 's/\([0-9]*\)+\([0-9]*\)/expr \1 + \2/e'
expr: 1000000000000000000000000000000: Numerical result out of range

Щоб подолати це обмеження, я просто звернувся до вбудованої потужності чистого sed і здійснив наступну десяткову сумацію довільної довжини:

#! / bin / sed -f

s / + / \ n / g
s / $ / \ n \ n0 /

: LOOP
s / ^ \ (. * \) \ (. \) \ n \ (. * \) \ (. \) \ n \ (. * \) \ n \ (. \) $ / 0 \ 1 \ n0 \ 3 \ n \ 5 \ n \ 6 \ 2 \ 4 /
год
s /^.* \ n. * \ n. * \ n \ (... \) $ / \ 1 /

# десятковий повний модуль суматора
# ВХОД: 3digits (Введіть, A, B,)
# ВИХІД: 2 біти (Нести, Сума)
s / $ /;000 = 00001 = 01002 = 02003 = 03004 = 04005 = 05006 = 06007 = 07008 = 08009 = 09010 = 01011 = 02012 = 03013 = 04014 = 05015 = 06016 = 07017 = 08018 = 09019 = 10020 = 02021 = 03022 = 04023 = 04023 = 05024 = 05024 = 06025 = 07026 = 08027 = 09028 = 10029 = 11030 = 03031 = 04032 = 05033 = 06034 = 07035 = 08036 = 09037 = 10038 = 11039 = 12040 = 04041 = 05042 = 06043 = 07044 = 08045 = 09046 = 10047 = 11048 = 12049 = 13050 = 05051 = 06052 = 07053 = 08054 = 09055 = 10056 = 11057 = 12058 = 13059 = 14060 = 06061 = 07062 = 08063 = 09064 = 10065 = 11066 = 12067 = 13068 = 14069 = 15070 = 07071 = 08072 = 09073 = 10074 = 11075 = 12076 = 13077 = 14078 = 15079 = 16080 = 08081 = 09082 = 10083 = 11084 = 12085 = 13086 = 14087 = 15088 = 16089 = 17090 = 09091 = 10092 = 11093 = 12094 = 13095 = 14096 = 15097 = 16098 = 17099 = 18100 = 01101 = 02102 = 03103 = 04104 = 05105 = 06106 = 07107 = 08108 = 09109 = 10110 = 02111 = 03112 = 04113 = 05114 = 06115 = 07116 = 08117 = 09118 = 10119 = 11120 = 03121 = 04122 = 05123 = 06123 = 06123 = 06123 07125 = 08126 = 09127 = 10128 = 11129 = 12130 = 04131 = 05132 = 06133 = 07134 = 08135 = 09136 = 10137 = 11138 = 12139 = 13140 = 05141 = 06142 = 07143 = 08144 = 09145 = 10146 = 11147 = 12148 = 13149 = 14150 = 06151 = 07152 = 08153 = 09154 = 10155 = 11156 = 12157 = 13158 = 14159 = 15160 = 07161 = 08162 = 09163 = 10164 = 11165 = 12166 = 13167 = 14168 = 15169 = 16170 = 08171 = 09172 = 10173 = 11174 = 12175 = 13176 = 14177 = 15178 = 16179 = 17180 = 09181 = 10182 = 11183 = 12184 = 13185 = 14186 = 15187 = 16188 = 17189 = 18190 = 10191 12193 = 13194 = 14195 = 15196 = 16197 = 17198 = 18199 = 19 /
s / ^ \ (... \) [^;] *; [^;] * \ 1 = \ (.. \). * / \ 2 /
Н
г
s / ^ \ (. * \) \ n \ (. * \) \ n \ (. * \) \ n ... \ n \ (. \) \ (. \) $ / \ 1 \ n \ 2 \ n \ 5 \ 3 \ n \ 4 /
/ ^ \ ([0] * \) \ n \ ([0] * \) \ n / {
        s /^.* \ n. * \ n \ (. * \) \ n \ (. \) / \ 2 \ 1 /
        s / ^ 0 \ (. * \) / \ 1 /
        q
}
b LOOP

Як це працює, реалізуючи модуль десяткового суматора, який додає дві вхідні цифри (A і B), а також Carry Bit і виробляє біт Sum і Carry. Ідея запозичена з електронних, де двійковий суматор робить те саме для двійкових чисел. Все, що нам потрібно зробити, це провести циклічне додавання на всі цифри, і ми можемо додати довільні числа довжини (обмежені пам'яттю). Нижче розміщено суматор:

./decAdder.sed
666666666666666666666666666666999999999999991111111112222+1100000000000000000000011111111111111111111111111111111111
1766666666666666666666677777778111111111111102222222223333

Точно так само можна реалізувати двійковий (або будь-який інший базовий) суматор. Все, що вам потрібно зробити, це замінити лінію, яка починається s/$/;000=00001...з відповідної схеми заміни для даної бази. Наприклад: s/$/;000=00001=01010=01011=10100=01101=10110=10111=11/ це схема заміни двійкового суматора довільної довжини.

Ви можете помістити код, задокументований на моїй github .

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