Ruby Regexp сопоставление групп, присваивать переменные в 1 строке - PullRequest
111 голосов
/ 16 февраля 2012

В настоящее время я пытаюсь преобразовать строку в несколько переменных. Пример строки:

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)

Возможно ли это? Или есть способ получше чем я?

Ответы [ 5 ]

181 голосов
/ 16 февраля 2012

Вы не хотите 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#match вернет ноль, поэтому что-то вроде этого может работать лучше:

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

Хотя scan имеет мало смысла для этого. Он все еще выполняет свою работу, вам просто нужно сначала сгладить возвращенный массив. one, two, three = string.scan(/(^.*)(:)(.*)/i).flatten

39 голосов
/ 16 февраля 2012

Вы можете использовать Match или = ~ вместо этого, что даст вам одно совпадение, и вы можете либо получить доступ к данным совпадения, либо просто использовать специальные переменные соответствия $ 1, $ 2, $ 3

Что-то вроде:

if ryan_string =~ /(^.*)(:)(.*)/i
   first = $1
   third = $3
end
27 голосов
/ 28 января 2014

Вы можете назвать ваши захваченные совпадения

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

Не работает, если вы измените порядок строк и регулярных выражений.

6 голосов
/ 02 октября 2015

Вы должны решить, является ли это хорошей идеей, но регулярное выражение ruby ​​может (автоматически) определить локальные переменные для вас!

Я еще не уверен, является ли эта функция удивительной или просто сумасшедшей, но ваш регулярный оператор может определять локальные переменные.

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).). Я не думаю, что я «крал», но если вы хотите быть справедливым с похвалами и Почитай первый ответ, не стесняйся :) Надеюсь, никто из животных не пострадал.

3 голосов
/ 16 февраля 2012

scan() найдет все неперекрывающиеся совпадения с регулярным выражением в вашей строке, поэтому вместо того, чтобы возвращать массив ваших групп, как вы ожидаете, он возвращает массив массивов.

Возможно, вам лучше использовать match(), а затем получить массив перехватов, используя MatchData#captures:

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

Однако вы также можете сделать это с scan(), если хотите:

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