2016-04-11 21 views
2

Bir karakteri bir karakter dizisiyle başka bir karakterle değiştirmek istiyorum, ancak karakter yalnızca dizenin sınırlandırılmış bir alt dizesi içinde gerçekleştiğinde. Örneğin, dize için:Bir dizgede bir karakter nasıl değiştirilir, ancak yalnızca ayrılmış bir alt dizede gerçekleşirse?

b [b] abc [abc] bbb [bbb]

Ben "b" değiştirmek istiyorum "x", ama sadece o kare içinde ise hiç parantez "[...]". Böylece, istenilen sonuç dizesi:

b [x] abc onlar benim rahatlık bölgesinde olduğu için [AXC] bbb [xxx]

Benim tercih bir sed veya bash çözüm olacaktır, ama Mac OS X için çalışacak herhangi bir çözüm iyi olurdu. Aramadan, sanki olumsuz bir bakış ve olumsuz bakış açısı kullanarak sed ile yapılabilir gibi görünüyor, ama bu özelliklerin sed'in Mac sürümünde mevcut olduğuna inanmıyorum.

+0

Evet. @pjh bir Perl çözümü sağladı. – scolfax

+0

Çeşitli çözümler sağlandı. Kişinin kişisel tercihlerine göre bir çözüm seçebilir. – scolfax

cevap

2

sed:

$ sed -r ':a;s/(\[[^]]*)b/\1x/;ta' <<< "b[b]abc[abc]bbb[bbb]" 
b[x]abc[axc]bbb[xxx] 
  • :a yaklaşan döngü için bir etiket ekler
  • s: yerine komut
  • (\[[^]]*): arama ve herhangi olmayan ] karakteri ardından [ yakalama
  • tarihine kadar b bulundu
  • eşleştirme dize başlangıçta yakalanan dize ile değiştirilir ve x
  • ta bir önceki ikamesi başarılı olursa, :a etiketlemek için döngüler OS X üzerinde sed GNU için

(başka b oluşumunu değiştirin):

daha fazla bilgi için
brew uninstall gnu-sed 

: How to use GNU sed on Mac OS X

0

gnu-awk kullanarak:

s='b[b]abc[abc]bbb[bbb]' 
awk -v OFS= -v FPAT='\\[[^]]+\\]|[^[]*' '{ 
    for (i=1; i<=NF; i++) if ($i ~ /\[.*\]/) gsub(/b/, "x", $i)} 1' <<< "$s" 

Çıktı: OSX'te

b[x]abc[axc]bbb[xxx] 

Ben ettik gnu-awk home brew kullanarak yüklü. GNU için

1

Bu bir (çok kaba kuvvet) pu Bash çözümü re:

raw='b[b]abc[abc]bbb[bbb]' 
cooked= 

declare -r delimited_rx='^(.*)\[([^][]*)\](.*)$' 

while [[ $raw =~ $delimited_rx ]] ; do 
    raw=${BASH_REMATCH[1]} 
    printf -v cooked '[%s]%s%s' \ 
     "${BASH_REMATCH[2]//b/x}" \ 
     "${BASH_REMATCH[3]}" \ 
     "$cooked" 
done 

cooked=$raw$cooked 

printf '%s\n' "$cooked" 
+0

Kötü bir fikir değil, ancak kodlanmış olarak üç parantezli eşleşme ile sınırlı. Döngünün işleyişini döngüde küçük bir tek eşleme ifadesine ayırırsanız, hala herhangi bir yer değiştirme olup olmadığını görmek ve ardından herhangi bir dizeyi ayrıştırmak için '$ {# BASH_REMATCH [@]}' seçeneğini işaretleyebilirsiniz. –

+0

@ A.Danischewski, "Üç parantezli kibrit" ile ne demek istediğini anlamıyorum. Kodun çalışmadığı bir örnek giriş dizesi verebilir misiniz? – pjh

+0

Aslında kodunuz iyi çalışıyor gibi görünüyor, çünkü zaten son parantez içi eşleşmeyle eşleşiyor ve devam ediyor. –

0
$ awk '{ while(match($0,/\[[^][]*b[^][]*\]/)) { tgt=substr($0,RSTART,RLENGTH); gsub(/b/,"x",tgt); $0=substr($0,1,RSTART-1) tgt substr($0,RSTART+RLENGTH) } } 1' file 
b[x]abc[axc]bbb[xxx] 
0

müthiş çözümler için teşekkür ederiz! Tüm çözümler (sed, awk ve bash) sistemimde mükemmel çalışır. Ben, sed için biraz kısmi olduğum için, sed çözümünü t komutuyla buluyorum ve çok güzel olmak için döngü oluşturuyorum.Biraz değiştirilmeli, yani değiştirilmelidir; linefeeds ve -E ile -r seçeneği yerine birlikte, benim OS X sistem üzerinde işe gitmek için: Ben temin ederim bir başka değişiklik yapılmış

sed -E ' 
:a 
s/(\[[^]]*)b/\1x/ 
ta 
' <<< "b[b]abc[abc]bbb[bbb]" 

b[x]abc[axc]bbb[xxx] 

o ikame yeri sadece kapanış köşeli ayraç eğer sürer bir açılış köşeli ayraç eşlik: "iyi olurdu Mac OS X için çalışacak herhangi bir çözümün" yana

sed -E ' 
:a 
s/(\[[^]]*)b([^]]*\])/\1x\2/ 
ta 
' <<< "b[b]abc[abc]bbb[bbb]bbb[bbb" 

b[x]abc[axc]bbb[xxx]bbb[bbb 
+0

“Sed” çözümü güzel (ve önerdiği cevabı kabul etmek de güzel olurdu), ama çok iyi değil. Örneğin, sınırlandırılmış b karakterlerini (yani 'x' yerine 'bb' yerine) iki katına çıkarmak için kullanmayı deneyin. Her yinelemede yaptığı tam yeniden tarama, büyük giriş dizelerine uygulandığında performans sorunlarına da neden olabilir. – pjh

+0

Bir 'bb' ikame dizisi ile sonsuz döngü içeren sed yaklaşımındaki sakıncaları işaretlediğiniz için teşekkür ederiz! Diğer yandan, bash ve perl metodlarınız, bir 'bb' ikame dizisini düzgün bir şekilde ele aldı. Genel bir çözüm olarak diğerini tavsiye eder misiniz? – scolfax

+0

@pjh Belki bir şeyi özledim ama ilk niyet b '-> 'bb' çifte değil, başka bir karakterle' x' karakteri değiştirmek oldu. – SLePort

1

, Perl düşünün:

perl -ple 's{\[([^][]*)\]}{ ($m=$1)=~s/b/x/g; "[$m]" }eg' <<< 'b[b]abc[abc]bbb[bbb]' 
0
echo 'b[b]abc[abc]bbb[bbb]' | awk -vRS='[][]' 'NR%2==0{gsub("b","x")}{printf $0 RT}' 
b[x]abc[axc]bbb[xxx] 
+0

Bir izlemde, OP, çözümün "ikame işleminin yalnızca bir açılış köşeli parantezin bir açılış köşeli parantez içine eşlik etmesi durumunda gerçekleşmesi gerektiğini" yazdı. Bu çözüm bunu yapmaz. Aslında, parantezleri hiçbir şekilde eşleştirmeyi denemez (örneğin, bbb] '' xxx] 'e dönüştürülür). – pjh