Normal ifadenin tüm tekrarları nasıl eşleştirilir?


586

Ruby'de normal bir ifadenin her eşleşmesini bulmanın hızlı bir yolu var mı? Ruby STL'deki Regex nesnesini inceledim ve Google'da boşuna arama yaptım.


3
Bu tüm regex desenleri için bir dize nasıl arayabilir ve korkunç bir şekilde karıştı ...
Hugoagogo

Yanıtlar:


821

Kullanarak scanhile yapmak gerekir:

string.scan(/regex/)

9
Ama bu davaya ne demeli? "benimle eşleş!". tara (/.../) = ["mat", "ch" "ben!" ], ancak /.../ tüm oluşumları ["mat", "atc", "tch", "ch", ...] olacaktır
Michael Dickens

13
Olmaz. /.../ normal bir açgözlü ifadedir. Eşleştirilen içerik üzerinde geri izleme yapılmaz. tembel bir normal ifade kullanmaya çalışabilirsiniz ama bu bile muhtemelen yeterli olmayacaktır. regexp doğru ifade etmek için regexp doc ruby-doc.org/core-1.9.3/Regexp.html bir göz atın :)
Jean

49
Bu bir Ruby WTF gibi görünüyor ... neden diğer regexp malzeme ile Regexp yerine String üzerinde bu?
Regexp

9
Sanırım bunun nedeni Regex'te değil String'de tanımlanmış ve çağrılmış ... Ama aslında mantıklı. Regex # eşleşmesini kullanarak tüm eşleşmeleri yakalamak için normal bir ifade yazabilir ve yakalanan gruplar üzerinden yineleyebilirsiniz. Burada kısmi bir eşleme işlevi yazıyorsunuz ve belirli bir dizede birden çok kez uygulanmasını istiyorsunuz, bu Regexp'in sorumluluğu değildir. Daha iyi bir anlayış için tarama uygulamasını kontrol etmenizi öneririm: ruby-doc.org/core-1.9.3/String.html#method-i-scan
Jean

9
@MichaelDickens: Bu durumda kullanabilirsiniz /(?=(...))/.
Konrad Borowski

67

Eşleşen tüm dizeleri bulmak için Dize kullanın scan yöntemini .

str = "A 54mpl3 string w1th 7 numb3rs scatter36 ar0und"
str.scan(/\d+/)
#=> ["54", "3", "1", "7", "3", "36", "0"]

İsterseniz MatchData, Regexp tarafından döndürülen nesnenin türü budurmatch yöntem, kullanım:

str.to_enum(:scan, /\d+/).map { Regexp.last_match }
#=> [#<MatchData "54">, #<MatchData "3">, #<MatchData "1">, #<MatchData "7">, #<MatchData "3">, #<MatchData "36">, #<MatchData "0">]

Kullanmanın avantajı MatchDatasizin gibi yöntemleri kullanabilirsiniz olmasıdıroffset :

match_datas = str.to_enum(:scan, /\d+/).map { Regexp.last_match }
match_datas[0].offset(0)
#=> [2, 4]
match_datas[1].offset(0)
#=> [7, 8]

Daha fazla bilgi edinmek istiyorsanız şu sorulara bakın:

Özel değişkenlerle ilgili okuma $&, $', $1, $2Ruby çok yararlı olacaktır.


12

gruplarla normal ifadeniz varsa:

str="A 54mpl3 string w1th 7 numbers scatter3r ar0und"
re=/(\d+)[m-t]/

scaneşleşen grupları bulmak için String'in yöntemini kullanabilirsiniz:

str.scan re
#> [["54"], ["1"], ["3"]]

Eşleşen kalıbı bulmak için:

str.to_enum(:scan,re).map {$&}
#> ["54m", "1t", "3r"]

str.scan(/\d+[m-t]/) # => ["54m", "1t", "3r"]daha deyimselstr.to_enum(:scan,re).map {$&}
Tin Man

Belki yanlış anladın. Yanıtladığım kullanıcı örneğinin normal ifadesi şuydu: Yazmak için /(\d+)[m-t]/değil /\d+[m-t]/: re = /(\d+)[m-t]/; str.scan(re)aynı str.scan(/(\d+)[mt]/)ama #> alıyorum [["" 54 "], [" 1 "], [" 3 "]]ve değil "54m", "1t", "3r"]. Soru şuydu: bir grupla düzenli bir ifadem varsa ve ifadesini (gruptan ayrılmak) nasıl yapabilirim? Bu anlamda, şifreli ve okunması zor olsa da olası bir çözüm şöyleydi:str.to_enum(:scan,re).map {$&}
MVP

-1

Kullanabilirsiniz string.scan(your_regex).flatten. Normal ifadeniz grup içeriyorsa, tek bir düz dizide dönecektir.

string = "A 54mpl3 string w1th 7 numbers scatter3r ar0und"
your_regex = /(\d+)[m-t]/
string.scan(your_regex).flatten
=> ["54", "1", "3"]

Normal ifade de adlandırılmış bir grup olabilir.

string = 'group_photo.jpg'
regex = /\A(?<name>.*)\.(?<ext>.*)\z/
string.scan(regex).flatten

Ayrıca kullanabilirsiniz gsub, MatchData istiyorsanız sadece bir yol daha.

str.gsub(/\d/).map{ Regexp.last_match }

Gruplandırmayı kaldırın ve your_regex = /(\d+)[m-t]/kullanmanıza gerek yoktur flatten. Son örnekte, last_matchbu durumda muhtemelen güvenli olan, ancak genel olan ve aramadan önce herhangi bir normal ifade eşleştirildiyse üzerine yazılabilen kullanım örnekleri kullanılır last_match. Bunun yerine , desene ve ihtiyaçlara bağlı olarak kullanımı daha güvenli string.match(regex).captures # => ["group_photo", "jpg"]veya string.scan(/\d+/) # => ["54", "3", "1", "7", "3", "0"]diğer cevaplarda gösterildiği gibi.
Tin Man
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.