Дополнение с «Сед»


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 за вызов, люби это! Может быть, это было бы что-то для Code Golf ;-p
Татьяна Хойзер

6
И некоторые люди говорят, что программирование не математика. Этот маленький драгоценный камень опровергает их всех. Лучшее использование Base 1
Брюс Эдигер

1
Хороший! - @Simon: Я призываю вас , чтобы реализовать тетрацию : P
AT

16
+1 Это прекрасный пример того, что может породить заблуждение в сочетании с творчеством.
rozcietrzewiacz

1
Умножение должно быть сделано с помощью sed - и оно тоже очень хорошо масштабируется!
Тоби Спейт

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
Цифровая травма

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'
Гленн Джекман


6

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

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

или

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

Я думаю, что это может потребовать GNU sed, но я не уверен.


Это расширение GNU.
Кевин

Хорошо, вы правы, но ответ выходит за рамки, он реализует общее добавление, а не конкретное, вы можете
подать

@LuigiTiburzi Довольно просто обобщить это для ввода в стиле "x + y":echo 12+3 | sed -r 's/([0-9]*) *\+ *([0-9]*)/expr \1 + \2/e'
Цифровая травма

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/')))

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

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

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


2
Там нет никакого башизма. $((...))был введен POSIX (bashism есть $[...]). ${var//xxx/x}это kshism, также скопированный zsh и bash. sed -rэто GNUism
Стефан

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 / $ / \ п \ n0 /

: LOOP
s / ^ \ (. * \) \ (. \) \ п \ (. * \) \ (. \) \ п \ (. * \) \ п \ (. \) $ / 0 \ 1 \ n0 \ 3 \ п \ 5 \ п \ 6 \ 2 \ 4 /
час
s /^.* \ п. * \ п. * \ п \ (... \) $ / \ 1 /

# десятичный модуль полного сумматора
# INPUT: 3digits (Carry in, A, B,)
# ВЫХОД: 2bit (Carry, Sum)
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 = 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 = 06124 = = 08126 07125 = 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 = 11192 = 12193 = 13194 = 14195 = 15196 = 16197 = 17198 = 18199 = 19 /
s / ^ \ (... \) [^;] * [^;]. * \ 1 = \ (.. \) * / \ 2 /
ЧАС
г
s / ^ \ (. * \) \ п \ (. * \) \ п \ (. * \) \ п ... \ п \ (. \) \ (. \) $ / \ 1 \ п \ 2 \ п \ 5 \ 3 \ п \ 4 /
/ ^ \ ([0] * \) \ n \ ([0] * \) \ n / {
        с /^.* \ п. * \ п \ (. * \) \ п \ (. \) / \ 2 \ 1 /
        s / ^ 0 \ (. * \) / \ 1 /
        Q
}
B LOOP

Это работает путем реализации модуля десятичного сумматора, который добавляет две входные цифры (A и B), а также бит переноса и выдает бит суммы и переноса. Идея заимствована из электронного, где двоичный сумматор делает то же самое для двоичных чисел. Все, что нам нужно сделать, это зациклить сумматор на всех цифрах, и мы можем добавить числа произвольной длины (ограниченные памятью). Ниже сумматор в действии:

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