Ngoài ra với 'sed'


40

Tôi đang cố gắng thực hiện một phép toán với sed, nhưng nó tiếp tục coi các biến của tôi là các chuỗi. Đầu vào là loại này:

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

Tôi muốn có 15 như đầu ra. Tôi cần thực hiện thao tác và thay thế kết quả toán học của nó chỉ trong một đoạn, bởi vì tôi đang chạy chương trình dưới dạng Python daemon và tôi muốn tránh các đoạn như chuyển hướng stdouttrên tệp, mở các tệp đó, thực hiện các thao tác, trích xuất kết quả, làm sự thay thế. Đối với tôi, seddường như là tốt nhất để thực hiện tất cả trong một dòng.

Tôi đã cố gắng truyền cả đầu vào và đầu ra theo nhiều cách khác nhau như

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

nhưng kết quả luôn luôn là một bản in của lĩnh vực thứ hai.


12
Nó coi "biến" của bạn là chuỗi vì tất cả đều là sed - thao tác chuỗi. Nó không có khái niệm về "số nguyên."
Kevin

2
Tôi rất tò mò tại sao bạn muốn sử dụng sedđể làm toán
David Oneill

Tôi chỉ nghĩ rằng nó có thể dễ dàng tạo ra các biến, không nhận ra là quá phức tạp!
Luigi Tiburzi

Câu trả lời:


82

Nếu bạn thực sự muốn sử dụng sed, thì đây là cách nên làm:

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

Đầu vào:

1+2
100+250
100-250

Đầu ra:

3
350
-150

Nhiệm vụ của bạn, nếu bạn chọn chấp nhận nó, là thực hiện phép nhân.


5
+1 cho thử thách, yêu nó! Có lẽ đó là một cái gì đó cho Code Golf ;-p
Tatjana Heuser

6
Và một số người nói rằng lập trình không phải là toán học. Viên ngọc nhỏ này bác bỏ tất cả. Sử dụng tốt nhất Base 1 từ trước đến nay.
Bruce Ediger

1
Đẹp quá - @Simon: Tôi thách bạn thực hiện tetration : P
AT

16
+1 Đây là một ví dụ đẹp về những gì một quan niệm sai lầm kết hợp với sự sáng tạo có thể sinh ra.
rozcietrzewiacz

1
Phép nhân được thực hiện với sed - và nó cũng có quy mô rất lớn!
Toby Speight

20

sedkhông phải là lựa chọn tốt nhất ở đây, nó không làm mỹ phẩm tự nhiên (xem Tăng số cho cách bạn có thể làm điều đó mặc dù). Bạn có thể làm điều đó với awk:

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

Đoạn mã tốt nhất để sử dụng sẽ phụ thuộc vào định dạng chính xác của đầu vào của bạn và những gì bạn muốn / cần làm nếu nó không phải là số hoặc chứa nhiều hơn một số, v.v.

Bạn cũng có thể làm điều này chỉ với bash:

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

hoặc sử dụng exprtheo cách tương tự.


17

Tôi đã cố chấp nhận thử thách của bạn @Richter, đây là những gì tôi đã làm bằng cách sử dụng một phần mã của bạn:

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'

Đầu vào:

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

Đầu ra: tất cả các kết quả chính xác


@SimonRichter hy vọng bạn sẽ thích !!
Luigi Tiburzi

Cross đã đăng câu trả lời tuyệt vời này tại đây: codegolf.stackexchange.com/a/39882/11259
Chấn thương kỹ thuật số

12

perlcho phép một cấu trúc rất giống với sed... một điểm khác biệt là perlcó thể làm những việc phức tạp hơn ... sedrất tốt cho các trạm biến áp văn bản đơn giản

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

đầu ra

a15

2
cũng có thể làm điều này mà không cần dấu ngoặc đơn:perl -pe 's/[0-9]+/$&+3/e'
glenn jackman

8

chỉ cần đưa chuỗi vào máy tính

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

Điều này sẽ không hoạt động nếu có văn bản trong số các số.
glenn jackman

6

Tôi thực sự không hiểu tại sao sự phức tạp cực độ của câu trả lời được chấp nhận, một trong những điều dưới đây làm những gì bạn muốn:

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

hoặc là

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

Tôi nghĩ rằng nó có thể yêu cầu GNU sed, nhưng tôi không chắc chắn.


Nó là một phần mở rộng gnu.
Kevin

Ok bạn đúng nhưng câu trả lời vượt xa, nó thực hiện bổ sung chung không phải là một số cụ thể, bạn có thể cung cấp bất kỳ hai số nào và bạn sẽ nhận được kết quả
Luigi Tiburzi

@LuigiTiburzi Khá đơn giản để khái quát điều này thành kiểu đầu vào "x + y":echo 12+3 | sed -r 's/([0-9]*) *\+ *([0-9]*)/expr \1 + \2/e'
Chấn thương kỹ thuật số

5

Nếu bạn chắc chắn phải kết hợp các biểu thức chính quy và các phép toán số học, hãy chọn một ngôn ngữ trong đó tham số thay thế của biểu thức chính quy có thể là hàm gọi lại.

Perl, Ruby, JavaScript và Python là những ngôn ngữ như vậy:

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

Một bashgiải pháp đơn giản khác , thực sự hoạt động trong một đường ống:

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

1

Nếu bạn trộn trong một số bashism:

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

Để trích xuất số từ một văn bản:

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

Không có sed, chỉ cần bash:

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

thay thế mọi chữ số ${var//[^0-9]/}và không tính số học trong các phép tính tròn kép:$((x+3))


2
Không có bashism trong đó. $((...))được giới thiệu bởi POSIX (bashism là $[...]). ${var//xxx/x}là một kshism cũng được sao chép bởi zsh và bash. sed -rlà một chủ nghĩa GNU
Stéphane Chazelas

0

Đây là một giải pháp Perl:

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

Nếu bạn muốn thay đổi bộ chữ số đầu tiên gặp phải trong một chuỗi, bạn có thể sử dụng:

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

Nếu bạn muốn thay đổi tất cả các bộ chữ số trong một chuỗi, bạn có thể sử dụng công cụ /gsửa đổi, như sau:

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

Mặc dù sử dụng biểu hiện sed là tuyệt vời nhưng nó có những hạn chế. Ví dụ sau thất bại:

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

Để khắc phục hạn chế này, tôi chỉ cần chuyển sang sức mạnh của sed thuần túy và thực hiện theo bộ cộng thập phân có độ dài tùy ý:

#! / bin / sed -f

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

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

# mô đun cộng đầy đủ thập phân
# INPUT: 3digits (Mang theo, A, B,)
# ĐẦU RA: 2 bit (Thực hiện, Tổng hợp)
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 = 06025 = 07026 = 08027 = 09028 = 10029 = 11030 = 03031 = 04032 = 05033 = 06034 = 07035 = 08036 = 09037 = 10038 = 11039 = 12040 = 04041 = 05042 = 06043 = 070 13050 = 05051 = 06052 = 07053 = 08054 = 09055 = 10056 = 11057 = 12058 = 13059 = 14060 = 06061 = 07062 = 08063 = 09064 = 10065 = 11066 = 12067 = 13068 = 14069 = 15070 = 070 = = 11075 = 12076 = 13077 = 14078 = 15079 = 16080 = 08081 = 09082 = 10083 = 11084 = 12085 = 13086 = 14087 = 15088 = 16089 = 17090 = 09091 = 10092 = 11093 = 12094 = 13095 = 14096 = 15097 = 18100 = 01101 = 02102 = 03103 = 04104 = 05105 = 06106 = 07107 = 08108 = 09109 = 10110 = 02111 = 03112 = 04113 = 05114 = 06115 = 07116 = 08117 = 09118 = 10119 = 11120 = 03121 = 04122 = 05123 = 07125 = 08126 = 09127 = 10128 = 11129 = 12130 = 04131 = 05132 = 06133 = 07134 = 08135 = 09136 = 10137 = 11138 = 12139 = 13140 = 05141 = 06142 = 0714 = =3 = 08144 = 09145 = 10146 = 11147 = 12148 = 13149 = 14150 = 06151 = 07152 = 08153 = 09154 = 10155 = 11156 = 12157 = 13158 = 14159 = 15160 = 11161 = 08162 = 09163 = 10164 = 11165 = 14168 = 15169 = 16170 = 08171 = 09172 = 10173 = 11174 = 12175 = 13176 = 14177 = 15178 = 16179 = 17180 = 09181 = 10182 = 11183 = 12184 = 13185 = 14186 = 15187 = 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

Cách thức hoạt động của nó là bằng cách triển khai mô đun bộ cộng thập phân có thêm hai chữ số đầu vào (A và B) cũng như Carry Bit và tạo ra bit Sum và Carry. Ý tưởng được mượn từ điện tử trong đó bộ cộng nhị phân thực hiện tương tự cho số nhị phân. Tất cả những gì chúng ta phải làm là lặp bộ cộng trên tất cả các chữ số và chúng ta có thể thêm các số có độ dài tùy ý (giới hạn bởi bộ nhớ). Dưới đây là trình bổ sung đang hoạt động:

./decAdder.sed
666666666666666666666666666666999999999999991111111112222+1100000000000000000000011111111111111111111111111111111111
1766666666666666666666677777778111111111111102222222223333

Trong cùng một cách chính xác, người ta có thể thực hiện bộ cộng nhị phân (hoặc bất kỳ cơ sở nào khác). Tất cả bạn phải làm là thay thế dòng bắt đầu bằng s/$/;000=00001...mẫu thay thế phù hợp cho cơ sở nhất định. Ví dụ: s/$/;000=00001=01010=01011=10100=01101=10110=10111=11/ là mẫu thay thế cho bộ cộng nhị phân có độ dài tùy ý.

Bạn có thể phù hợp với mã được ghi lại trên github của tôi .

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.