Выбрать главу

Метод класса 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?]/ # Сопоставляется с точкой, символом новой строки,

         # вопросительным знаком.

Символ каре (^) внутри класса символов имеет специальный смысл, если находится в начале; в этом случае он формирует дополнение к списку символов:

[^aeiou] # Любой символ, КРОМЕ а, е, i, о, и.

Дефис внутри класса символов обозначает диапазон (в лексикографическом порядке):

/[а-mA-М]/  # Любой символ из первой половины алфавита.

/[^а-mA-М]/ # Любой ДРУГОЙ символ, а также цифры и символы. отличные

            # от букв и цифр.

Дефис в начале или в конце класса символов, а также каре в середине теряют специальный смысл и интерпретируются буквально. То же относится к левой квадратной скобке, но правая квадратная скобка, очевидно, должна экранироваться:

/[-^[\]]/ # Сопоставляется с дефисом, каре и правой квадратной скобкой.

Регулярные выражения в Ruby могут содержать ссылки на именованные классы символов вида [[:name:]]. Так, [[:digit:]] означает то же самое, что образец [0-9]. Во многих случаях такая запись оказывается короче или, по крайней мере, понятнее.

Есть еще такие именованные классы: [[:print:]] (символы, имеющие графическое начертание) и [[:alpha:]] (буквы):

s1 = "abc\007def"

/[[:print:]]*/.match(s1)

m1 = Regexp::last_match[0] # "abc"

s2 = "1234def"

/[[:digit:]]*/.match(s2)

m2 = Regexp::last_match[0] # "1234"

/[[:digit:]] + [[:alpha:]]/.match(s2)

m3 = Regexp::last_match[0] # "1234d"

Каре перед именем класса символов формирует его дополнение:

/[[:^alpha:]]/ # Все символы, кроме букв.