Cara mencocokkan semua kemunculan sebuah regex

586

Apakah ada cara cepat untuk menemukan setiap kecocokan ekspresi reguler di Ruby? Saya telah melihat melalui objek Regex di STL Ruby dan mencari di Google tidak berhasil.

Chris Bunch
sumber
3
Saya membaca ini bagaimana saya bisa mencari string untuk semua pola regex dan sangat bingung ...
Hugoagogo

Jawaban:

821

Penggunaan scanharus melakukan trik:

string.scan(/regex/)
Jean
sumber
9
Tapi apa yang berbatasan dengan kasus ini? "cocokkan saya!". scan (/.../) = ["mat", "ch" "me!" ], tetapi semua kejadian dari / .../ akan menjadi ["mat", "atc", "tch", "ch", ...]
Michael Dickens
13
Bukan itu tidak akan. /.../ adalah regexp serakah yang normal. Itu tidak akan mundur pada konten yang cocok. Anda bisa mencoba menggunakan regexp yang malas tetapi bahkan itu mungkin tidak cukup. lihat regexp doc ruby-doc.org/core-1.9.3/Regexp.html untuk mengekspresikan regexp Anda dengan benar :)
Jean
49
ini tampak seperti WTF Ruby ... mengapa ini di String bukannya Regexp dengan hal-hal regexp lainnya? Bahkan tidak disebutkan di mana pun di dokumen untuk Regexp
Anentropic
9
Saya kira itu karena itu didefinisikan dan dipanggil pada String bukan pada Regex ... Tapi itu sebenarnya masuk akal. Anda dapat menulis ekspresi reguler untuk menangkap semua pertandingan menggunakan Regex # cocok dan beralih ke grup yang ditangkap. Di sini Anda menulis fungsi kecocokan parsial dan ingin itu diterapkan beberapa kali pada string yang diberikan, ini bukan tanggung jawab Regexp. Saya sarankan Anda memeriksa implementasi pemindaian untuk pemahaman yang lebih baik: ruby-doc.org/core-1.9.3/String.html#method-i-scan
Jean
9
@MichaelDickens: Dalam hal ini, Anda dapat menggunakan /(?=(...))/.
Konrad Borowski
67

Untuk menemukan semua string yang cocok, gunakan scanmetode String .

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

Jika Anda ingin,, MatchDatayang merupakan jenis objek yang dikembalikan oleh matchmetode Regexp , gunakan:

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

Manfaat menggunakan MatchDataadalah Anda dapat menggunakan metode seperti offset:

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]

Lihat pertanyaan ini jika Anda ingin tahu lebih banyak:

Membaca tentang variabel khusus $&, $', $1, $2di Ruby akan membantu juga.

sudo bangbang
sumber
12

jika Anda memiliki regexp dengan grup:

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

Anda dapat menggunakan scanmetode String untuk menemukan grup yang cocok:

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

Untuk menemukan pola yang cocok:

str.to_enum(:scan,re).map {$&}
#> ["54m", "1t", "3r"]
MVP
sumber
str.scan(/\d+[m-t]/) # => ["54m", "1t", "3r"]lebih idiomatis daripadastr.to_enum(:scan,re).map {$&}
Tin Man
Mungkin Anda salah paham. Ekspresi reguler dari contoh pengguna yang saya jawab adalah: /(\d+)[m-t]/tidak /\d+[m-t]/Untuk menulis: re = /(\d+)[m-t]/; str.scan(re)sama str.scan(/(\d+)[mt]/)tetapi saya mendapatkan #> [["" 54 "], [" 1 "], [" 3 "]]dan tidak "54m", "1t", "3r"]Pertanyaannya adalah: jika saya memiliki ekspresi reguler dengan grup dan ingin menangkap semua pola tanpa mengubah yang biasa ekspresi (meninggalkan grup), bagaimana saya bisa melakukannya? Dalam hal ini, solusi yang mungkin, meskipun sedikit samar dan sulit dibaca, adalah:str.to_enum(:scan,re).map {$&}
MVP
-1

Anda bisa menggunakannya string.scan(your_regex).flatten. Jika regex Anda berisi grup, itu akan kembali dalam satu array polos.

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

Regex dapat berupa grup yang diberi nama juga.

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

Anda juga dapat menggunakan gsub, itu hanya satu cara lagi jika Anda ingin MatchData.

str.gsub(/\d/).map{ Regexp.last_match }
Datt
sumber
Hapus pengelompokan dari your_regex = /(\d+)[m-t]/dan Anda tidak perlu menggunakan flatten. Contoh terakhir Anda menggunakan last_matchyang dalam hal ini mungkin aman, tetapi bersifat global dan mungkin bisa ditimpa jika ada regex yang cocok sebelum menelepon last_match. Sebaliknya mungkin lebih aman untuk digunakan string.match(regex).captures # => ["group_photo", "jpg"]atau string.scan(/\d+/) # => ["54", "3", "1", "7", "3", "0"]seperti yang ditunjukkan dalam jawaban lain, tergantung pada pola dan kebutuhan.
the Tin Man