Обратите внимание на одиночные (твердые) кавычки в предыдущем примере. Если бы мы воспользовались двойными (мягкими) кавычками, не приняв никаких мер предосторожности, то элементы, которым предшествует обратная косая черта, были бы интерпретированы как восьмеричные числа:
При таком применении блока числа с обратной косой чертой нельзя использовать ни в двойных, ни в одиночных кавычках. Если вы немного поразмыслите, то поймете, что это разумно.
Упомяну попутно о том, что существуют незапоминаемые группы (noncapturing groups). Иногда при составлении регулярного выражения нужно сгруппировать символы, но чему будет соответствовать в конечном счете такая группа, несущественно. На этот случай и предусмотрены незапоминаемые группы, описываемые синтаксической конструкцией
В предыдущем примере вторая группа не запоминается, поэтому та группа, которая должна была бы быть третьей, становится второй.
Лично мне не нравится ни одна из двух нотаций (
\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
p4 = refs.end(2) # 17
# "delta "
p5 = refs.begin(3) # 17
p6 = refs.end(3) # 23
# "beta gamma delta"
p7 = refs.begin(0) # 6
p8 = refs.end(0) # 23
Аналогично метод
offset
возвращает массив из двух чисел: смещение начала и смещение конца соответствия. Продолжим предыдущий пример:
range0 = refs.offset(0) # [6,23]
range1 = refs.offset(1) # [6,11]
range2 = refs.offset(2) # [11,17]
range3 = refs.offset(3) # [17,23]
Части строки, которые находятся перед сопоставленной подстроки и после нее, можно получить методами
pre_match
и
post_match
соответственно. В том же коде:
before = refs.pre_match # "alpha "
after = refs.post_match # "epsilon"
3.8. Классы символов
Классы символов — это просто форма перечисления (указание альтернатив), в котором каждая группа состоит из одного символа. В простейшем случае список возможных символов заключается в квадратные скобки:
/[aeiou]/ # Соответствует любой из букв а, е, i, о, и; эквивалентно
# /(a|e|i|o|u)/, только группа не запоминается.
Внутри класса символов управляющие последовательности типа
\n
по-прежнему распознаются, но такие метасимволы, как
.
и
?
, не имеют специального смысла:
/[.\n?]/ # Сопоставляется с точкой, символом новой строки,