Ruby Regexp 그룹 매칭, 한 줄에 변수 할당


125

현재 문자열을 여러 변수로 rexp하려고합니다. 예제 문자열 :

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#match를 반환하는 것을 사용할 수 있으며 캡처 배열을 반환하도록 MatchData호출 할 수 있습니다 #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)
Andrea Salicetti 2013 년

5
@AndreaSalicetti은 내가 반환 전무 개체를 처리하기위한 버전으로 변경했다, 그래서 나는 그것에 레일 고유의 코드를 추가하고 있지 않다, 내 게시물을 편집 한
리 자비스

3
당신은 또한 새로운 &.오퍼레이터가 그것을 라인에 다시 가져오고 심지어 하나의 캡처 그룹이있을 때 두 번 사용할 수도 있습니다. 예.,string.match(regex)&.captures&.first
Gerry Shaw

46

당신은 사용할 수 있습니다 매치 또는 = ~ 대신에 당신에게 하나의 일치를 줄 것이며, 당신도 경기 데이터를 같은 방법으로 액세스하거나 특별한 경기 변수를 $ 1, $ 2, $ 3 사용할 수있는

다음과 같은 것 :

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

5
@Gaston은 실제로 Perl에서 시작된 원래 regexp 구문입니다. :)
ohaleck

28

캡처 한 일치의 이름을 지정할 수 있습니다.

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

문자열과 정규식의 순서를 반대로하면 작동하지 않습니다.


6

좋은 생각인지 결정해야하지만 루비 정규 표현식은 (자동으로) 지역 변수를 정의 할 수 있습니다. 를 있습니다!

이 기능이 굉장한 지 완전히 미친지는 아직 확실하지 않지만 정규식은 지역 변수를 정의 할 수 있습니다.

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.