Pencocokan grup Ruby Regexp, tetapkan variabel pada 1 baris

125

Saat ini saya mencoba rexp string menjadi beberapa variabel. Contoh string:

ryan_string = "RyanOnRails: This is a test"

Saya telah mencocokkannya dengan regexp ini, dengan 3 grup:

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

Sekarang untuk mengakses setiap grup saya harus melakukan sesuatu seperti ini:

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

Ini sepertinya sangat konyol dan rasanya saya melakukan sesuatu yang salah. Saya berharap dapat melakukan sesuatu seperti ini:

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

Apakah ini mungkin? Atau apakah ada cara yang lebih baik daripada cara saya melakukannya?

ryanjones.dll
sumber

Jawaban:

199

Anda tidak ingin scanini, karena tidak masuk akal. Anda dapat menggunakan String#matchyang akan mengembalikan MatchDataobjek, Anda kemudian dapat memanggil #capturesuntuk mengembalikan Array tangkapan. Sesuatu seperti ini:

#!/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"

Ketahuilah bahwa jika tidak ada kecocokan yang ditemukan, String#matchakan mengembalikan nol, jadi sesuatu seperti ini mungkin bekerja lebih baik:

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

Meskipun scantidak masuk akal untuk ini. Itu masih melakukan pekerjaan, Anda hanya perlu meratakan Array yang dikembalikan terlebih dahulu.one, two, three = string.scan(/(^.*)(:)(.*)/i).flatten

Lee Jarvis
sumber
6
Berhati-hatilah jika tidak ada kecocokan yang ditemukan, kecocokan mengembalikan nol dan Anda mendapatkan NilError. Jika Anda berada di Rails, saya sarankan Anda untuk mengganti: one, two, three = string.match(/(^.*)(:)(.*)/i).captures menjadi: one, two, three = string.match(/(^.*)(:)(.*)/i).try(:captures)
Andrea Salicetti
5
@AndreaSalicetti Saya telah mengedit posting saya, saya tidak menambahkan kode khusus Rails ke dalamnya jadi saya telah mengubahnya dengan versi untuk menangani objek nihil yang dikembalikan
Lee Jarvis
3
Anda juga dapat menggunakan &.operator baru untuk mendapatkannya kembali di jalur dan bahkan menggunakannya dua kali ketika hanya ada satu grup tangkapan. Misalnya ..,string.match(regex)&.captures&.first
Gerry Shaw
46

Anda dapat menggunakan Match atau = ~ sebagai gantinya yang akan memberi Anda satu kecocokan dan Anda bisa mengakses data kecocokan dengan cara yang sama atau hanya menggunakan variabel kecocokan khusus $ 1, $ 2, $ 3

Sesuatu seperti:

if ryan_string =~ /(^.*)(:)(.*)/i
   first = $1
   third = $3
end
Rado
sumber
5
@Gaston itu sebenarnya sintaks regexp asli yang berasal dari Perl :)
ohaleck
28

Anda dapat memberi nama jodoh Anda

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

Ini tidak berfungsi jika Anda membalik urutan string dan regex.

toonsend
sumber
6

Anda harus memutuskan apakah itu ide yang bagus, tetapi ruby ​​regexp dapat (secara otomatis) mendefinisikan variabel lokal untuk Anda!

Saya belum yakin apakah fitur ini keren atau benar-benar gila, tetapi regex Anda dapat menentukan variabel lokal.

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}"

(Lihat http://ruby-doc.org/core-2.1.1/Regexp.html , cari "variabel lokal").

Catatan: Seperti yang ditunjukkan dalam komentar, saya melihat bahwa ada jawaban serupa dan sebelumnya untuk pertanyaan ini oleh @toonsend ( https://stackoverflow.com/a/21412455 ). Saya tidak berpikir saya "mencuri", tetapi jika Anda ingin bersikap adil dengan pujian dan kehormatan jawaban pertama, silakan :) Saya harap tidak ada hewan yang dirugikan.

Felix
sumber
Jawaban ini terlihat sangat mirip dengan stackoverflow.com/a/21412455/525478 , yang lebih dari setahun lebih tua ...
Brad Werth
@BradWerth Saya rasa saya tidak melihatnya. Tapi saya memperbarui jawaban saya untuk memasukkan kekhawatiran Anda.
Felix
5

scan() akan menemukan semua kecocokan yang tidak tumpang tindih dari regex dalam string Anda, jadi alih-alih mengembalikan larik grup Anda seperti yang Anda harapkan, ini mengembalikan larik array.

Anda mungkin lebih baik menggunakan match(), dan kemudian mendapatkan rangkaian tangkapan menggunakan MatchData#captures:

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

Namun Anda juga bisa melakukan ini scan()jika Anda ingin:

g1, g2, g3 = ryan_string.scan(/(^.*)(:)(.*)/i)[0]
Andrew Clark
sumber