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 может быть довольно длинным, что не обязательно означает, что оно будет относительно неэффективным, так как предугадывание довольно локализовано.