Tambahan dengan 'sed'

40

Saya mencoba melakukan operasi matematika dengan sed, tetapi terus memperlakukan variabel saya sebagai string. Masukan dari jenis ini:

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

Saya ingin memiliki 15 sebagai output. Saya perlu melakukan operasi dan mengganti hasil matematika hanya dalam satu bagian, karena saya menjalankan program sebagai daemon Python, dan saya ingin menghindari bagian seperti mengarahkan ulang stdoutpada file, membuka file-file itu, melakukan operasi, mengekstrak hasilnya, lakukan penggantian. Bagi saya, sedsepertinya yang terbaik untuk melakukan semuanya dalam satu baris.

Saya sudah mencoba melakukan input dan output dengan berbagai cara seperti

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

tetapi hasilnya selalu berupa cetakan bidang kedua.

Luigi Tiburzi
sumber
12
Ini memperlakukan "variabel" Anda sebagai string karena hanya itu yang dilakukan - manipulasi string. Ia tidak memiliki konsep "integer."
Kevin
2
Saya sangat ingin tahu mengapa Anda ingin menggunakan sedmatematika
David Oneill
Saya hanya berpikir itu bisa dengan mudah melemparkan variabel, tidak menyadari begitu rumit!
Luigi Tiburzi

Jawaban:

82

Jika Anda benar-benar ingin menggunakan sed, maka inilah caranya:

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

Memasukkan:

1+2
100+250
100-250

Keluaran:

3
350
-150

Misi Anda, jika Anda memilih untuk menerimanya, adalah menerapkan perkalian.

Simon Richter
sumber
5
Beri +1 untuk tantangan, menyukainya! Mungkin itu akan menjadi sesuatu untuk Code Golf ;-p
Tatjana Heuser
6
Dan beberapa orang mengatakan bahwa pemrograman itu bukan matematika. Permata kecil ini membantah mereka semua. Penggunaan terbaik dari Basis 1.
Bruce Ediger
1
Yang bagus! - @Simon: Saya menantang Anda untuk menerapkan tetrasi : P
AT
16
+1 Ini adalah contoh indah tentang bagaimana kesalahpahaman yang dipasangkan dengan kreativitas dapat berkembang biak.
rozcietrzewiacz
1
Perkalian harus dilakukan dengan sed - dan itu skala ke jumlah yang sangat besar juga!
Toby Speight
20

sedbukan pilihan terbaik di sini, itu tidak melakukan aritmatika secara asli (lihat Menambah nomor untuk bagaimana Anda bisa melakukannya). Anda dapat melakukannya dengan awk:

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

Sepotong kode terbaik untuk digunakan akan tergantung pada format yang tepat dari input Anda dan apa yang ingin / perlu Anda lakukan jika itu bukan angka, atau mengandung lebih dari satu angka, dll.

Anda juga dapat melakukan ini hanya dengan bash:

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

atau menggunakan exprcara yang serupa.

Tikar
sumber
17

Saya mencoba menerima tantangan Anda @Richter, inilah yang saya lakukan menggunakan bagian dari kode Anda:

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'

Memasukkan:

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

Output: semua hasil yang benar

Luigi Tiburzi
sumber
@SimonRichter harap Anda menikmati !!
Luigi Tiburzi
Cross memposting jawaban cemerlang ini di sini: codegolf.stackexchange.com/a/39882/11259
Digital Trauma
12

perlmemungkinkan untuk konstruksi yang sangat mirip dengan sed... satu perbedaan adalah bahwa perldapat melakukan hal - hal yang lebih kompleks ... sedsangat baik untuk subtitle teks sederhana

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

keluaran

a15
Peter.O
sumber
2
dapat juga melakukan ini tanpa tanda kurung:perl -pe 's/[0-9]+/$&+3/e'
glenn jackman
8

masukkan saja string ke dalam kalkulator

 echo 12 | sed 's/[0-9]*/&+3/' | bc
glenn jackman
sumber
ini tidak akan berfungsi jika ada teks di antara angka-angka.
glenn jackman
6

Saya benar-benar tidak mengerti mengapa kompleksitas ekstrim dari jawaban yang diterima, salah satu di bawah ini melakukan apa yang Anda inginkan:

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

atau

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

Saya pikir itu mungkin memerlukan sed GNU, tapi saya tidak yakin.

michelpm
sumber
Ini adalah ekstensi gnu.
Kevin
Ok Anda benar tetapi jawabannya melampaui, itu menerapkan penambahan umum bukan yang khusus, Anda dapat memberi makan dua nomor dan Anda akan mendapatkan hasilnya
Luigi Tiburzi
@LuigiTiburzi Cukup mudah untuk menggeneralisasikan ini ke input gaya "x + y":echo 12+3 | sed -r 's/([0-9]*) *\+ *([0-9]*)/expr \1 + \2/e'
Digital Trauma
5

Jika Anda benar-benar harus menggabungkan ekspresi reguler dan operasi aritmatika, pilih bahasa di mana parameter penggantian ekspresi reguler dapat menjadi fungsi callback.

Perl, Ruby, JavaScript, dan Python adalah bahasa-bahasa tersebut:

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
manatwork
sumber
1

bashSolusi sederhana lain , yang benar-benar berfungsi di pipa:

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

Jika Anda mencampur beberapa bashism:

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

Untuk mengekstrak nomor dari teks:

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

Tanpa sed, cukup bash:

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

menggantikan setiap non-digit ${var//[^0-9]/}dan melakukan aritmatika dalam parens putaran ganda:$((x+3))

Pengguna tidak diketahui
sumber
2
Tidak ada bashism di sana. $((...))diperkenalkan oleh POSIX (bashismenya $[...]). ${var//xxx/x}adalah kshism juga disalin oleh zsh dan bash. sed -radalah GNUism
Stéphane Chazelas
0

Inilah solusi Perl:

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

Jika Anda lebih suka mengubah set digit pertama yang ditemukan dalam sebuah string, Anda dapat menggunakan:

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

Jika Anda lebih suka mengubah semua set digit dalam sebuah string, Anda dapat menggunakan /gpengubah, seperti ini:

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.
JL
sumber
0

Meskipun menggunakan ekspresi sed itu bagus itu memiliki keterbatasan. Misalnya gagal berikut:

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

Untuk mengatasi keterbatasan ini, saya cukup beralih ke kekuatan bawaan sed murni dan mengimplementasikan berikut ini:

#! / bin / sed -f

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

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

# modul penambah penuh desimal
# INPUT: 3 digit (Carry in, A, B,)
# OUTPUT: 2bits (Carry, Sum)
s / $ /;000 = 00001 = 01002 = 02003 = 03004 = 04005 = 05006 = 06007 = 07008 = 08009 = 01011 = 01012 = 02012 = 04015 = 06016 = 06016 = 07017 = 08018 = 02019 = 02022 = 02022 = 03022 = 04022 = 020 06025 = 07026 = 08027 = 09028 = 10029 = 11030 = 03031 = 04033 = 05034 = 06035 = 08036 = 08037 = 09037 = 10039 = 12040 = 04041 = 05042 = 06044 = 09045 = 09045 = 09045 = 09046 = 11048 = 12048 = 0 13050 = 05051 = 06052 = 07053 = 08054 = 09055 = 10057 = 11057 = 13058 = 14060 = 06061 = 07062 = 08063 = 09064 = 10065 = 11066 = 12067 = 13068 = 14069 = 07071 = 09072 = 09073 = 10074 = 100 11075 = 12076 = 13077 = 14078 = 15079 = 16080 = 08081 = 09082 = 10083 = 11084 = 13086 = 14087 = 15088 = 16089 = 17089 = 17090 = 09091 = 10093 = 11094 = 12094 = 13095 = 14096 = 15098 = 17099 = 18100 = 01101 = 02102 = 03103 = 04104 = 05105 = 06106 = 07107 = 08108 = 09109 = 10110 = 02111 = 03112 = 05114 = 06115 = 07116 = 08117 = 08117 = 10119 = 10119 = 0612 = 0512 07125 = 08126 = 09127 = 10128 = 11129 = 12130 = 04131 = 05132 = 06133 = 07134 = 08135 = 09136 = 10137 = 11138 = 12139 = 13140 = 05141 = 06142 = 071423 = 08144 = 09145 = 10146 = 11147 = 12148 = 13149 = 14150 = 06151 = 07152 = 08153 = 09154 = 10155 = 11156 = 12157 = 13158 = 14159 = 15160 = 07161 = 08162 = 10164 = 11165 = 1216 = 1216 = 12 14168 = 15169 = 16170 = 08171 = 09172 = 10173 = 11174 = 12175 = 13176 = 14177 = 15178 = 16179 = 17180 = 09181 = 10182 = 11183 = 12184 = 13185 = 14186 = 15187 = 16188 = 1718 = 1018 = 12193 = 13194 = 14195 = 15196 = 16197 = 17198 = 18199 = 19 /
s / ^ \ (... \) [^;] *; [^;] * \ 1 = \ (.. \). * / \ 2 /
H
g
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

Cara kerjanya adalah dengan menerapkan modul penambah desimal yang menambahkan dua digit input (A dan B) serta Carry Bit dan menghasilkan Sum dan Carry bit. Idenya dipinjam dari elektronik di mana biner adder melakukan hal yang sama untuk nomor biner. Yang harus kita lakukan adalah melingkari penambah di atas semua angka dan kita dapat menambahkan angka panjang acak (dibatasi oleh memori). Di bawah ini adalah penambah yang sedang beraksi:

./decAdder.sed
666666666666666666666666666666999999999999991111111112222+1100000000000000000000011111111111111111111111111111111111
1766666666666666666666677777778111111111111102222222223333

Dengan cara yang persis sama seseorang dapat mengimplementasikan adder biner (atau basis lainnya). Yang harus Anda lakukan adalah mengganti garis yang dimulai dengan s/$/;000=00001...pola substitusi yang tepat untuk basis yang diberikan. Sebagai contoh: s/$/;000=00001=01010=01011=10100=01101=10110=10111=11/ adalah pola substitusi untuk penambah panjang biner sewenang-wenang.

Anda dapat memasukkan kode yang didokumentasikan di github saya .

Emsi
sumber