Сослаться на группы можно с помощью глобальных переменных $1
, $2
и т.д:
str = "а123b45с678"
if /(a\d+)(b\d+)(c\d+)/ =~ str
puts "Частичные соответствия: '#$1', '#$2', '#$3'"
# Печатается: Частичные соответствия: 'а123', 'b45', 'c768'
end
Эти переменные нельзя использовать в подставляемой строке в методах sub
и gsub
:
str = "а123b45с678"
str.sub(/(a\d+)(b\d+)(c\d+)/, "1st=#$1, 2nd=#$2, 3rd=#$3")
# "1st=, 2nd=, 3rd="
Почему такая конструкция не работает? Потому что аргументы sub
вычисляются перед вызовом sub
. Вот эквивалентный код:
str = "а123b45с678"
s2 = "1st=#$1, 2nd=#$2, 3rd=#$3"
reg = /(a\d+)(b\d+)(c\d+)/
str.sub(reg,s2)
# "1st=, 2nd=, 3rd="
Отсюда совершенно понятно, что значения $1
, $2
, $3
никак не связаны с сопоставлением, которое делается внутри вызова sub.
В такой ситуации на помощь приходят специальные коды \1
, \2
и т.д.:
str = "а123b45с678"
str.sub(/(a\d+)(b\d+)(c\d+)/, '1st=\1, 2nd=\2, 3rd=\3')
# "1st=a123, 2nd=b45, 3rd=c768"
Обратите внимание на одиночные (твердые) кавычки в предыдущем примере. Если бы мы воспользовались двойными (мягкими) кавычками, не приняв никаких мер предосторожности, то элементы, которым предшествует обратная косая черта, были бы интерпретированы как восьмеричные числа:
str = "а123b45с678"
str.sub(/(a\d+)(b\d+)(c\d+)/, "1st=\1, 2nd=\2, 3rd=\3")
# "1st=\001, 2nd=\002, 3rd=\003"
Обойти эту неприятность можно за счет двойного экранирования:
str = "а123b45с678"
str.sub(/(a\d+)(b\d+)(c\d+)/, "1st=\\1, 2nd=\\2, 3rd=\\3")
# "1st=a123, 2nd=b45, 3rd=c678"
Допустима и блочная форма подстановки, в которой можно использовать глобальные переменные:
str = "а123b45с678"
str.sub(/(a\d+)(b\d+)(c\d+)/) { "1st=#$1, 2nd=#$2, 3rd=#$3" }
# "1st=a123, 2nd=b45, 3rd=c678"
При таком применении блока числа с обратной косой чертой нельзя использовать ни в двойных, ни в одиночных кавычках. Если вы немного поразмыслите, то поймете, что это разумно.
Упомяну попутно о том, что существуют незапоминаемые группы (noncapturing groups). Иногда при составлении регулярного выражения нужно сгруппировать символы, но чему будет соответствовать в конечном счете такая группа, несущественно. На этот случай и предусмотрены незапоминаемые группы, описываемые синтаксической конструкцией (?:...)
:
str = "а123b45с678"
str.sub(/(a\d+)(?:b\d+)(c\d+)/, "1st=\\1, 2nd=\\2, 3rd=\\3")
# "1st=a123, 2nd=c678, 3rd="
В предыдущем примере вторая группа не запоминается, поэтому та группа, которая должна была бы быть третьей, становится второй.
Лично мне не нравится ни одна из двух нотаций ( \1
и $1
). Иногда они удобны, но никогда не бывают необходимы. Все можно сделать «красивее», в объектно-ориентированной манере.
Метод класса Regexp.last_match
возвращает объект класса MatchData
(как и метод экземпляра match
). У этого объекта есть методы экземпляра, с помощью которых программист может получить обратные ссылки.
Обращаться к объекту MatchData
можно с помощью квадратных скобок, как если бы это был массив соответствий. Специальный элемент с индексом 0 содержит текст всей сопоставляемой строки, а элемент с индексом n ссылается на n-ую запомненную группу:
pat = /(. + [aiu])(.+[aiu])(.+[aiu])(.+[aiu])/i
#
В этом образце есть четыре одинаковых группы.
refs = pat.match("Fujiyama")
# refs is now: ["Fujiyama","Fu","ji","ya","ma"]
x = refs[1]
y = refs[2..3]
refs.to_a.each {|x| print "#{x}\n"}
Отметим, что объект refs
— не настоящий массив. Поэтому, если мы хотим обращаться с ним как с таковым, применяя итератор each
, следует сначала преобразовать его в массив с помощью метода to_a
(как показано в примере).
Есть и другие способы нахождения сопоставленной подстроки внутри исходной строки. Методы begin
и end
возвращают смещения начала и конца соответствия. (Важно понимать, что смещение конца — это индекс символа, следующего за найденным соответствием.)
str = "alpha beta gamma delta epsilon"
# 0....5....0....5....0....5....
# (для удобства подсчета)
pat = /(b[^ ]+ )(g[^ ]+ )(d[^ ]+ )/
# Три слова, каждое из которых представляет собой отдельное соответствие.
refs = pat.match(str)
# "beta "
p1 = refs.begin(1) # 6
p2 = refs.end(1) # 11
# "gamma "
p3 = refs.begin(2) # 11
Читать дальше
Конец ознакомительного отрывка
Купить книгу