Как проверить формат строки в Ruby, извлекая совпадения? - PullRequest
0 голосов
/ 15 февраля 2019

Что я хочу

  • проверить, что строка соответствует этому формату: /^(#\d\s*)+$/ (например, #1 #2).

  • Grabвсе числа с хешем, что-то вроде #<MatchData "1234" 1:"#1" 2:"#2">.Это не обязательно должен быть объект MatchData, любой тип массива, перечисляемый будет работать.

Моя проблема

При использовании match он просто соответствует последнему вхождению:

/^(#\d\s*)+$/.match "#1 #2"
# => #<MatchData "#1 #2" 1:"#2">

Когда я использую сканирование, оно "работает":

"#1 #2".scan /#\d/
# => ["#1", "#2"]

Но я не верю, что смогу проверить формат строки, так как она вернет то же самое для "aaa #1 #2".

Вопрос

Могу ли я одним вызовом метода подтвердить, что моя строка соответствует /^(#\d\s*)+$/ И захватить все экземпляры #number?

Я немного расстроен, когда спрашиваю об этом, поскольку уже некоторое время пользуюсь рубином.Это кажется простым, но я не могу заставить это работать.

Ответы [ 2 ]

0 голосов
/ 18 февраля 2019
def doit(str)
  r = /\A#{"(#\\d)\\s*"*str.count('#')}\z/      
  str.match(r)&.captures
end

doit "#1#2 #3 "    #=> ["#1", "#2", "#3"]
doit " #1#2 #3 "   #=> nil

Обратите внимание, что регулярные выражения зависят только от количества экземпляров символа '#' в строке.Поскольку в обоих примерах это число равно трем, соответствующие регулярные выражения равны, а именно:

/\A(#\d)\s*(#\d)\s*(#\d)\s*\z/

Это регулярное выражение было построено следующим образом.

str = "#1#2 #3 "
n = str.count('#')
  #=> 3
s = "(#\\d)\\s*"*n
  #=> "(#\\d)\\s*(#\\d)\\s*(#\\d)\\s*" 
/\A#{s}\z/ 
  #=> /\A(#\d)\s*(#\d)\s*(#\d)\s*\z/ 

Регулярное выражение читает "matchначало строки, за которым следуют три идентичные группы захвата, за каждой из которых, возможно, следуют пробелы, после которых следует конец строки. Поэтому регулярное выражение проверяет правильность строки и извлекает нужные совпадения в группах захвата.

Оператор безопасной навигации , & необходим в случае отсутствия совпадения (match возвращает nil).

Комментарий OP относится кобобщение вопроса, в котором символ фунта ('#') является необязательным. Это можно решить, изменив регулярное выражение следующим образом.

def doit(str)
  r = /\A#{"(?:#?(\\d)(?=#|\\s+|\\z)\\s*)"*str.count('0123456789')}\z/
  str.match(r)&.captures
end

doit "1 2 #3 "     #=> ["1", "2", "3"] 
doit "1 2 #3 "     #=> ["1", "2", "3"] 
doit "1#2"         #=> ["1", "2"] 
doit " #1 2 #3 "   #=> nil   
doit "#1 2# 3 "    #=> nil 
doit " #1 23 #3 "  #=> nil 

Для строк, содержащих три цифры, регулярное выражение:

/\A(?:#?(\d)(?=#|\s+|\z)\s*)(?:#?(\d)(?=#|\s+|\z)\s*)(?:#?(\d)(?=#|\s+|\z)\s*)\z/ 

Хотя это верноВыражение egular может быть довольно длинным, что не обязательно означает, что оно будет относительно неэффективным, так как предугадывание довольно локализовано.

0 голосов
/ 15 февраля 2019

Да, вы можете использовать

s.scan(/(?:\G(?!\A)|\A(?=(?:#\d\s*)*\z))\s*\K#\d/)

См. Демоверсию regex

Подробнее

  • (?:\G(?!\A)|\A(?=(?:#\d\s*)*\z)) - две альтернативы:
    • \G(?!\A) - конец предыдущего успешного совпадения
    • | - или
    • \A(?=(?:#\d\s*)*\z) - начало строки (\A), за которым следует 0 или более повторений # + цифр + 0+ пробелов, а затем конец строки
  • \s* - 0+ пробелов
  • \K - оператор сброса совпадений, отбрасывающий сопоставленный текст
  • #\d - символ #, а затем цифра

Короче: началопервой позиции строки соответствует, но только если строка справа (то есть вся строка) соответствует желаемому шаблону.Так как эта проверка выполняется с предварительным просмотром, индекс регулярного выражения остается там, где он был, и затем сопоставление происходит все время ТОЛЬКО после действительного совпадения благодаря оператору \G (оно соответствует началу строки или концу предыдущего совпадения, поэтому(?!\A) используется для вычитания начальной позиции строки.)

Ruby demo :

rx = /(?:\G(?!\A)|\A(?=(?:#\d\s*)*\z))\s*\K#\d/
p "#1 #2".scan(rx)
# => ["#1", "#2"]
p "#1 NO #2".scan(rx)
# => []
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...