Ruby Regexpグループマッチング、変数を1行で割り当てる


125

私は現在、文字列を複数の変数に再展開しようとしています。文字列の例:

ryan_string = "RyanOnRails: This is a test"

私はこれを次の3つのグループの正規表現と一致させました。

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

各グループにアクセスするには、次のようにする必要があります。

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

これはかなりばかげているようで、私は何か間違っているように感じます。私はこのようなことができると期待されます:

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

これは可能ですか?それとも私がそれをやっているよりも良い方法はありますか?

回答:


199

scanそれはほとんど意味をなさないので、これを望まないでしょう。を使用しString#matchMatchDataオブジェクト#capturesを返し、次にを呼び出してキャプチャの配列を返すことができます。このようなもの:

#!/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#matchnilが返されるため、次のような方が効果的であることに注意してください。

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

これにscanはほとんど意味がありませんが。それでも機能します。返された配列を最初にフラット化する必要があります。one, two, three = string.scan(/(^.*)(:)(.*)/i).flatten


6
一致が見つからない場合、matchはnilを返し、NilErrorが発生することに注意してください。あなたがRailsのであるならば、私は変更にあなたをお勧め: one, two, three = string.match(/(^.*)(:)(.*)/i).captures に: one, two, three = string.match(/(^.*)(:)(.*)/i).try(:captures)
アンドレアSalicetti

5
@AndreaSalicetti投稿を編集しました。Rails固有のコードは追加していません。そのため、返されたnilオブジェクトを処理するためのバージョンで変更しました
Lee Jarvis '25年

3
また、新しい&.演算子を使用して、それを1行に戻し、キャプチャグループが1つしかない場合は2回使用することもできます。例:string.match(regex)&.captures&.first
Gerry Shaw

46

代わりにMatchまたは=〜を使用すると、単一の一致が得られ、同じ方法で一致データにアクセスするか、特別な一致変数$ 1、$ 2、$ 3を使用できます。

何かのようなもの:

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

5
@Gastonそれは実際にはPerlから発信された元の正規表現構文です:)
ohaleck '31年

28

キャプチャした試合に名前を付けることができます

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

文字列と正規表現の順序を逆にすると機能しません。


6

それが良いアイデアかどうかを判断する必要がありますが、ruby regexpはローカル変数を(自動的に)定義できますをます!

この機能が素晴らしいのか、それともまったくおかしいのかはまだわかりませんが、正規表現でローカル変数を定義できます。

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

http://ruby-doc.org/core-2.1.1/Regexp.htmlを見て、「ローカル変数」を検索してください)。

注: コメントで指摘したように、@ toonsend(https://stackoverflow.com/a/21412455)によるこの質問に対する同様の以前の回答があることがわかります。私は「盗んだ」とは思っていませんが、賞賛に公平であり、最初の回答を尊重したい場合は、遠慮なくお問い合わせください。動物に害が及ばないことを願っています。


この回答は、1年以上前のstackoverflow.com/a/21412455/525478と非常によく似ています...
Brad Werth

@BradWerth私はそれを見なかったと思います。しかし、私はあなたの懸念を含めるために私の答えを更新しました。
Felix

5

scan() は、正規表現の重複しない一致をすべて文字列で検索するため、期待どおりのグループの配列を返す代わりに、配列の配列を返します。

おそらくmatch()、を使用してから、次を使用してキャプチャの配列を取得する方がよいでしょうMatchData#captures

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

ただし、次のことを実行しscan()たい場合は、これを使用することもできます。

g1, g2, g3 = ryan_string.scan(/(^.*)(:)(.*)/i)[0]
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.