Ruby Regexp grup eşleştirme, 1 satıra değişken atama


125

Şu anda bir dizeyi birden çok değişkene dönüştürmeye çalışıyorum. Örnek dize:

ryan_string = "RyanOnRails: This is a test"

Bunu 3 grupla bu regexp ile eşleştirdim:

ryan_group = ryan_string.scan(/(^.*)(:)(.*)/i)

Şimdi her gruba erişmek için şuna benzer bir şey yapmam gerekiyor:

ryan_group[0][0] (first group) RyanOnRails
ryan_group[0][1] (second group) :
ryan_group[0][2] (third group) This is a test

Bu oldukça saçma görünüyor ve yanlış bir şey yapıyormuşum gibi geliyor. Bunun gibi bir şey yapabilmeyi beklerdim:

g1, g2, g3 = ryan_string.scan(/(^.*)(:)(.*)/i)

Mümkün mü? Yoksa benim yaptığımdan daha iyi bir yolu var mı?

Yanıtlar:


199

scanBiraz mantıklı olduğu için bunu istemezsin . String#matchHangisinin bir MatchDatanesneyi döndüreceğini kullanabilir , ardından #capturesbir Yakalama Dizisi döndürmek için çağırabilirsiniz . Bunun gibi bir şey:

#!/usr/bin/env ruby

string = "RyanOnRails: This is a test"
one, two, three = string.match(/(^.*)(:)(.*)/i).captures

p one   #=> "RyanOnRails"
p two   #=> ":"
p three #=> " This is a test"

String#matchEşleşme bulunmazsa sıfır döneceğinden, bunun gibi bir şey daha iyi çalışabilir:

if match = string.match(/(^.*)(:)(.*)/i)
  one, two, three = match.captures
end

Yine scande bunun için pek mantıklı değil. Hala işi yapıyor, sadece önce döndürülen Diziyi düzleştirmeniz gerekiyor.one, two, three = string.scan(/(^.*)(:)(.*)/i).flatten


6
Eşleşme bulunmazsa, maçın sıfır döndüğüne ve bir NilError aldığınıza dikkat edin. one, two, three = string.match(/(^.*)(:)(.*)/i).capturesone, two, three = string.match(/(^.*)(:)(.*)/i).try(:captures)
Rails'teyseniz

5
@AndreaSalicetti Gönderimi düzenledim, ona Rails'e özgü kod eklemiyorum, bu yüzden geri dönen nil nesnesini işlemek için bir sürümle değiştirdim
Lee Jarvis

3
Ayrıca, yeni &.operatörün bir hatta geri getirmesi ve hatta tek bir yakalama grubu olduğunda onu iki kez kullanması da mümkündür. Örn ..,string.match(regex)&.captures&.first
Gerry Shaw

46

Bunun yerine size tek bir eşleşme verecek olan Match veya = ~ kullanabilirsiniz ve eşleşme verilerine aynı şekilde erişebilir veya sadece $ 1, $ 2, $ 3 özel eşleşme değişkenlerini kullanabilirsiniz.

Gibi bir şey:

if ryan_string =~ /(^.*)(:)(.*)/i
   first = $1
   third = $3
end

5
@Gaston bu aslında Perl'den kaynaklanan orijinal regexp sözdizimi :)
ohaleck

28

Yakaladığınız eşleşmeleri adlandırabilirsiniz

string = "RyanOnRails: This is a test"
/(?<one>^.*)(?<two>:)(?<three>.*)/i =~ string
puts one, two, three

Dize ve normal ifadenin sırasını ters çevirirseniz işe yaramaz.


6

Bunun iyi bir fikir olup olmadığına karar vermelisiniz, ancak ruby ​​regexp yerel değişkenleri (otomatik olarak) tanımlayabilir sizin için !

Henüz bu özelliğin harika mı yoksa tamamen çılgın mı olduğundan emin değilim, ancak normal ifadeniz yerel değişkenleri tanımlayabilir.

ryan_string = "RyanOnRails: This is a test"
/^(?<webframework>.*)(?<colon>:)(?<rest>)/ =~ ryan_string
# This defined three variables for you. Crazy, but true.
webframework # => "RyanOnRails"
puts "W: #{webframework} , C: #{colon}, R: #{rest}"

(Bir göz atın http://ruby-doc.org/core-2.1.1/Regexp.html "Yerel değişken" araması).

Not: Bir yorumda belirtildiği gibi, bu soruya @toonsend ( https://stackoverflow.com/a/21412455 ) tarafından benzer ve daha erken bir cevap verildiğini görüyorum . "Hırsızlık" yaptığımı sanmıyorum, ancak övgülerle adil olmak ve ilk cevabı onurlandırmak istiyorsanız, çekinmeyin :) Umarım hiçbir hayvan zarar görmez.


Bu cevap , bir yıldan daha eski olan stackoverflow.com/a/21412455/525478'e oldukça benziyor ...
Brad Werth

@BradWerth sanırım bunu görmedim. Ancak cevabımı endişelerinizi de içerecek şekilde güncelledim.
Felix

5

scan() dizenizdeki normal ifadenin örtüşmeyen tüm eşleşmelerini bulacaktır, bu nedenle beklediğiniz gibi gruplarınızın bir dizisini döndürmek yerine, bir dizi dizisi döndürür.

Kullanmanız match()ve ardından yakalama dizisini kullanmanız muhtemelen daha iyidir MatchData#captures:

g1, g2, g3 = ryan_string.match(/(^.*)(:)(.*)/i).captures

Bununla birlikte, isterseniz bunu da yapabilirsiniz scan():

g1, g2, g3 = ryan_string.scan(/(^.*)(:)(.*)/i)[0]
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.