Совпадение групп Ruby Regexp, назначение переменных в 1 строке
В настоящее время я пытаюсь перерисовать строку в несколько переменных. Пример строки:
ryan_string = "RyanOnRails: This is a test"
Я сопоставил его с этим регулярным выражением с тремя группами:
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)
Возможно ли это? Или есть лучший способ, чем то, как я это делаю?
Ответы
Ответ 1
Вы не хотите 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
Ответ 2
Вместо этого вы можете использовать Match или = ~, что даст вам одно совпадение, и вы можете либо получить доступ к данным сопоставления таким же образом или просто используйте специальные переменные соответствия $1, $2, $3
Что-то вроде:
if ryan_string =~ /(^.*)(:)(.*)/i
first = $1
third = $3
end
Ответ 3
Вы можете назвать свои захваченные совпадения
string = "RyanOnRails: This is a test"
/(?<one>^.*)(?<two>:)(?<three>.*)/i =~ string
puts one, two, three
Это не работает, если вы меняете порядок строк и регулярное выражение.
Ответ 4
Вы должны решить, является ли это хорошей идеей, но регулярное выражение 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 (fooobar.com/questions/66890/...). Я не думаю, что я "воровал", но если вы хотите быть честным с похвалой и чествовать первый ответ, не стесняйтесь :) Я надеюсь, что никто из животных не пострадал.
Ответ 5
scan()
найдет все неперекрывающиеся соответствия регулярного выражения в вашей строке, поэтому вместо того, чтобы возвращать массив ваших групп, как вы, кажется, ожидаете, он возвращает массив массивов.
Вероятно, вам лучше использовать match()
, а затем получить массив захватов с помощью MatchData#captures
:
g1, g2, g3 = ryan_string.match(/(^.*)(:)(.*)/i).captures
Однако вы также можете сделать это с помощью scan()
, если хотите:
g1, g2, g3 = ryan_string.scan(/(^.*)(:)(.*)/i)[0]
Ответ 6
По умолчанию у вас есть доступ к глобальным переменным в соответствии с группами совпадений, что также может быть очень полезно:
"hello world".match /(hello)(\s)(world)/ # => #<MatchData "hello world" 1:"hello" 2:" " 3:"world">
$1 # => "hello"
$2 # => " "
$3 # => "world"
$4 # => nil
"hello #{$3}" # => "hello world"