класс Regexp
Регулярное выражение (также называемое regexp) — это шаблон сопоставления (или просто шаблон).
Распространённая запись regexp использует ограничивающие символы косой черты:
/foo/
Regexp можно применить к целевой строке; часть строки (если она есть), соответствующая шаблону, называется совпадением, а про неё говорят, что она соответствует шаблону:
re = /red/
re.match?('redirect') # => true # Match at beginning of target.
re.match?('bored') # => true # Match at end of target.
re.match?('credit') # => true # Match within target.
re.match?('foo') # => false # No match.
Применение Regexp
Regexp можно использовать:
-
Для извлечения подстрок на основе заданного шаблона:
re = /foo/ # => /foo/ re.match('food') # => #<MatchData "foo"> re.match('good') # => nilСм. разделы Метод match и Оператор =~.
-
Чтобы определить, соответствует ли строка заданному шаблону:
re.match?('food') # => true re.match?('good') # => falseСм. раздел Метод match?.
-
В качестве аргумента при вызовах некоторых методов других классов и модулей; большинство таких методов принимают аргумент, которым может быть строка или (гораздо более мощный) regexp.
См. Методы Regexp.
Объекты Regexp
Объект regexp имеет:
-
Исходное выражение; см. Исходные выражения.
-
Несколько режимов; см. Режимы.
-
Тайм-аут; см. Тайм-ауты.
-
Кодировку; см. Кодировки.
Создание Regexp
Регулярное выражение можно создать с помощью:
-
Литерала regexp с символами косой черты (см. Литералы Regexp):
# This is a very common usage. /foo/ # => /foo/
-
Литерала regexp
%r(см. %r: литералы Regexp):# Same delimiter character at beginning and end; # useful for avoiding escaping characters %r/name\/value pair/ # => /name\/value pair/ %r:name/value pair: # => /name\/value pair/ %r|name/value pair| # => /name\/value pair/ # Certain "paired" characters can be delimiters. %r[foo] # => /foo/ %r{foo} # => /foo/ %r(foo) # => /foo/ %r<foo> # => /foo/ -
Метода
Regexp.new.
Метод match
Каждый из методов Regexp#match, String#match и Symbol#match возвращает объект MatchData, если совпадение найдено, и nil в противном случае; каждый из них также устанавливает глобальные переменные:
'food'.match(/foo/) # => #<MatchData "foo"> 'food'.match(/bar/) # => nil
Оператор =~
Каждый из операторов Regexp#=~, String#=~ и Symbol#=~ возвращает целочисленное смещение, если совпадение найдено, и nil в противном случае; каждый из них также устанавливает глобальные переменные:
/bar/ =~ 'foo bar' # => 4 'foo bar' =~ /bar/ # => 4 /baz/ =~ 'foo bar' # => nil
Метод match?
Каждый из методов Regexp#match?, String#match? и Symbol#match? возвращает true, если совпадение найдено, и false в противном случае; ни один из них не устанавливает глобальные переменные:
'food'.match?(/foo/) # => true 'food'.match?(/bar/) # => false
Глобальные переменные
Некоторые методы, связанные с regexp, присваивают значения глобальным переменным:
-
match: см. Метод match. -
=~: см. Оператор =~.
Затрагиваемые глобальные переменные:
-
$~: возвращает объектMatchDataилиnil. -
$&: возвращает совпавшую часть строки илиnil. -
$`: возвращает часть строки слева от совпадения илиnil. -
$': возвращает часть строки справа от совпадения илиnil. -
$+: возвращает последнюю совпавшую группу илиnil. -
$1,$2и т. д.: возвращают первую, вторую и последующие совпавшие группы илиnil. Обратите внимание, что$0— совсем другое; она возвращает имя выполняемой программы.
Примеры:
# Matched string, but no matched groups.
'foo bar bar baz'.match('bar')
$~ # => #<MatchData "bar">
$& # => "bar"
$` # => "foo "
$' # => " bar baz"
$+ # => nil
$1 # => nil
# Matched groups.
/s(\w{2}).*(c)/.match('haystack')
$~ # => #<MatchData "stac" 1:"ta" 2:"c">
$& # => "stac"
$` # => "hay"
$' # => "k"
$+ # => "c"
$1 # => "ta"
$2 # => "c"
$3 # => nil
# No match.
'foo'.match('bar')
$~ # => nil
$& # => nil
$` # => nil
$' # => nil
$+ # => nil
$1 # => nil
Обратите внимание, что Regexp#match?, String#match? и Symbol#match? не устанавливают глобальные переменные.
Исходные выражения
Как было показано выше, в простейшем regexp в качестве исходного выражения используется литерал:
re = /foo/ # => /foo/
re.match('food') # => #<MatchData "foo">
re.match('good') # => nil
Богатый набор доступных подвыражений наделяет regexp большой мощностью и гибкостью:
Специальные символы
Специальные символы regexp, называемые метасимволами, в определённых контекстах имеют особое значение; в зависимости от контекста следующие символы иногда являются метасимволами:
. ? - + * ^ \ | $ ( ) [ ] { } Чтобы сопоставить метасимвол буквально, экранируйте его обратной косой чертой:
# Matches one or more 'o' characters.
/o+/.match('foo') # => #<MatchData "oo">
# Would match 'o+'.
/o\+/.match('foo') # => nil
Чтобы сопоставить обратную косую черту буквально, экранируйте её обратной косой чертой:
/\./.match('\.') # => #<MatchData ".">
/\\./.match('\.') # => #<MatchData "\\.">
Метод Regexp.escape возвращает экранированную строку:
Regexp.escape('.?-+*^\|$()[]{}')
# => "\\.\\?\\-\\+\\*\\^\\\\\\|\\$\\(\\)\\[\\]\\{\\}"
Литералы исходных выражений
Литерал исходного выражения во многом ведёт себя как строка в двойных кавычках; см. Строковые литералы в двойных кавычках.
В частности, литерал исходного выражения может содержать интерполированные выражения:
s = 'foo' # => "foo"
/#{s}/ # => /foo/
/#{s.capitalize}/ # => /Foo/
/#{2 + 2}/ # => /4/
Между обычным строковым литералом и литералом исходного выражения есть различия; см. Сокращённые классы символов.
-
\sв обычном строковом литерале эквивалентно пробелу; в литерале исходного выражения это сокращённая запись для сопоставления с пробельным символом. -
В обычном строковом литерале эти символы экранированы (без необходимости); в литерале исходного выражения они являются сокращёнными записями для сопоставления с различными символами:
\w \W \d \D \h \H \S \R
Классы символов
Класс символов ограничивается квадратными скобками; он задаёт символы, которые могут совпасть в заданной позиции целевой строки:
# This character class will match any vowel.
re = /B[aeiou]rd/
re.match('Bird') # => #<MatchData "Bird">
re.match('Bard') # => #<MatchData "Bard">
re.match('Byrd') # => nil
Класс символов может содержать дефисы, задающие диапазоны символов:
# These regexps have the same effect.
/[abcdef]/.match('foo') # => #<MatchData "f">
/[a-f]/.match('foo') # => #<MatchData "f">
/[a-cd-f]/.match('foo') # => #<MatchData "f">
Если первый символ класса символов — циркумфлекс (^), смысл класса инвертируется: он сопоставляется с любым символом, кроме указанных.
/[^a-eg-z]/.match('f') # => #<MatchData "f">
Класс символов может содержать другой класс символов. Само по себе это бесполезно, поскольку [a-z[0-9]] описывает тот же набор, что и [a-z0-9].
Однако классы символов также поддерживают оператор &&, который выполняет пересечение множеств своих аргументов. Их можно объединить следующим образом:
/[a-w&&[^c-g]z]/ # ([a-w] AND ([^c-g] OR z))
Это эквивалентно:
/[abh-w]/
Сокращённые классы символов
Каждый из следующих метасимволов служит сокращённой записью класса символов:
-
/./: соответствует любому символу, кроме символа новой строки:/./.match('foo') # => #<MatchData "f"> /./.match("\n") # => nil -
/./m: соответствует любому символу, включая символ новой строки; см. Многострочный режим:/./m.match("\n") # => #<MatchData "\n"> -
/\w/: соответствует буквенно-цифровому символу: эквивалентно[a-zA-Z0-9_]:/\w/.match(' foo') # => #<MatchData "f"> /\w/.match(' _') # => #<MatchData "_"> /\w/.match(' ') # => nil -
/\W/: соответствует небуквенно-цифровому символу: эквивалентно[^a-zA-Z0-9_]:/\W/.match(' ') # => #<MatchData " "> /\W/.match('_') # => nil -
/\d/: соответствует цифре: эквивалентно[0-9]:/\d/.match('THX1138') # => #<MatchData "1"> /\d/.match('foo') # => nil -
/\D/: соответствует символу, не являющемуся цифрой: эквивалентно[^0-9]:/\D/.match('123Jump!') # => #<MatchData "J"> /\D/.match('123') # => nil -
/\h/: соответствует шестнадцатеричной цифре: эквивалентно[0-9a-fA-F]:/\h/.match('xyz fedcba9876543210') # => #<MatchData "f"> /\h/.match('xyz') # => nil -
/\H/: соответствует символу, не являющемуся шестнадцатеричной цифрой: эквивалентно[^0-9a-fA-F]:/\H/.match('fedcba9876543210xyz') # => #<MatchData "x"> /\H/.match('fedcba9876543210') # => nil -
/\s/: соответствует пробельному символу: эквивалентно/[ \t\r\n\f\v]/:/\s/.match('foo bar') # => #<MatchData " "> /\s/.match('foo') # => nil -
/\S/: соответствует непробельному символу: эквивалентно/[^ \t\r\n\f\v]/:/\S/.match(" \t\r\n\f\v foo") # => #<MatchData "f"> /\S/.match(" \t\r\n\f\v") # => nil -
/\R/: соответствует переводу строки независимо от платформы:/\R/.match("\r") # => #<MatchData "\r"> # Carriage return (CR) /\R/.match("\n") # => #<MatchData "\n"> # Newline (LF) /\R/.match("\f") # => #<MatchData "\f"> # Formfeed (FF) /\R/.match("\v") # => #<MatchData "\v"> # Vertical tab (VT) /\R/.match("\r\n") # => #<MatchData "\r\n"> # CRLF /\R/.match("\u0085") # => #<MatchData "\u0085"> # Next line (NEL) /\R/.match("\u2028") # => #<MatchData "\u2028"> # Line separator (LSEP) /\R/.match("\u2029") # => #<MatchData "\u2029"> # Paragraph separator (PSEP)
Якоря
Якорь — это метапоследовательность, соответствующая позиции нулевой ширины между символами целевой строки.
Для подвыражения без якоря поиск совпадения может начаться в любом месте целевой строки:
/real/.match('surrealist') # => #<MatchData "real">
Для подвыражения с якорем поиск совпадения должен начинаться с позиции, соответствующей якорю.
Якоря границ
Каждый из этих якорей соответствует границе:
-
^: соответствует началу строки:/^bar/.match("foo\nbar") # => #<MatchData "bar"> /^ar/.match("foo\nbar") # => nil -
$: соответствует концу строки:/bar$/.match("foo\nbar") # => #<MatchData "bar"> /ba$/.match("foo\nbar") # => nil -
\A: соответствует началу строки:/\Afoo/.match('foo bar') # => #<MatchData "foo"> /\Afoo/.match(' foo bar') # => nil -
\Z: соответствует концу строки; если строка заканчивается одним символом новой строки, соответствует позиции перед этим завершающим символом новой строки:/foo\Z/.match('bar foo') # => #<MatchData "foo"> /foo\Z/.match('foo bar') # => nil /foo\Z/.match("bar foo\n") # => #<MatchData "foo"> /foo\Z/.match("bar foo\n\n") # => nil -
\z: соответствует концу строки:/foo\z/.match('bar foo') # => #<MatchData "foo"> /foo\z/.match('foo bar') # => nil /foo\z/.match("bar foo\n") # => nil -
\b: вне квадратных скобок соответствует границе слова; внутри скобок соответствует символу возврата на шаг ("0x08"):/foo\b/.match('foo bar') # => #<MatchData "foo"> /foo\b/.match('foobar') # => nil -
\B: соответствует позиции, не являющейся границей слова:/foo\B/.match('foobar') # => #<MatchData "foo"> /foo\B/.match('foo bar') # => nil -
\G: соответствует первой позиции поиска:В таких методах, как
String#gsubиString#scan, она изменяется при каждой итерации. Изначально она соответствует началу проверяемой строки, а на каждой следующей итерации — позиции окончания предыдущего совпадения." a b c".gsub(/ /, '_') # => "____a_b_c" " a b c".gsub(/\G /, '_') # => "____a b c"
В таких методах, как
Regexp#matchиString#match, принимающих необязательное смещение, она соответствует началу поиска."hello, world".match(/,/, 3) # => #<MatchData ","> "hello, world".match(/\G,/, 3) # => nil
Якоря просмотра окрестности
Якоря просмотра вперёд:
-
(?=pat): положительная проверка просмотра вперёд: гарантирует, что следующие символы соответствуют pat, но не включает эти символы в совпавшую подстроку. -
(?!pat): отрицательная проверка просмотра вперёд: гарантирует, что следующие символы не соответствуют pat, но не включает эти символы в совпавшую подстроку.
Якоря просмотра назад:
-
(?<=pat): положительная проверка просмотра назад: гарантирует, что предыдущие символы соответствуют pat, но не включает эти символы в совпавшую подстроку. -
(?<!pat): отрицательная проверка просмотра назад: гарантирует, что предыдущие символы не соответствуют pat, но не включает эти символы в совпавшую подстроку.
Приведённый ниже шаблон использует положительный просмотр вперёд и положительный просмотр назад для сопоставления текста внутри тегов …, не включая сами теги в совпадение:
/(?<=<b>)\w+(?=<\/b>)/.match("Fortune favors the <b>bold</b>.")
# => #<MatchData "bold">
Якорь сброса совпадения
-
\K: сброс совпадения: совпавшее содержимое перед\Kв regexp исключается из результата. Например, следующие два regexp почти эквивалентны:/ab\Kc/.match('abc') # => #<MatchData "c"> /(?<=ab)c/.match('abc') # => #<MatchData "c">Они соответствуют одной и той же строке, и
$&равно'c', но позиция совпадения различается.То же относится к следующим двум regexp:
/(a)\K(b)\Kc/ /(?<=(?<=(a))(b))c/
Альтернация
Метасимвол вертикальной черты (|) можно использовать внутри скобок для выражения альтернации: двух или более подвыражений, любое из которых может соответствовать целевой строке.
Две альтернативы:
re = /(a|b)/
re.match('foo') # => nil
re.match('bar') # => #<MatchData "b" 1:"b">
Четыре альтернативы:
re = /(a|b|c|d)/
re.match('shazam') # => #<MatchData "a" 1:"a">
re.match('cold') # => #<MatchData "c" 1:"c">
Каждая альтернатива — это подвыражение, которое может состоять из других подвыражений:
re = /([a-c]|[x-z])/
re.match('bar') # => #<MatchData "b" 1:"b">
re.match('ooz') # => #<MatchData "z" 1:"z">
Метод Regexp.union предоставляет удобный способ создать regexp с альтернативами.
Квантификаторы
Простой regexp соответствует одному символу:
/\w/.match('Hello') # => #<MatchData "H">
Добавленный квантификатор задаёт количество требуемых или допустимых совпадений:
-
*— соответствует ноль или более раз:/\w*/.match('') # => #<MatchData ""> /\w*/.match('x') # => #<MatchData "x"> /\w*/.match('xyz') # => #<MatchData "yz"> -
+— соответствует один или более раз:/\w+/.match('') # => nil /\w+/.match('x') # => #<MatchData "x"> /\w+/.match('xyz') # => #<MatchData "xyz"> -
?— соответствует ноль или один раз:/\w?/.match('') # => #<MatchData ""> /\w?/.match('x') # => #<MatchData "x"> /\w?/.match('xyz') # => #<MatchData "x"> -
{n}— соответствует ровно n раз:/\w{2}/.match('') # => nil /\w{2}/.match('x') # => nil /\w{2}/.match('xyz') # => #<MatchData "xy"> -
{min,}— соответствует min или более раз:/\w{2,}/.match('') # => nil /\w{2,}/.match('x') # => nil /\w{2,}/.match('xy') # => #<MatchData "xy"> /\w{2,}/.match('xyz') # => #<MatchData "xyz"> -
{,max}— соответствует max раз или менее:/\w{,2}/.match('') # => #<MatchData ""> /\w{,2}/.match('x') # => #<MatchData "x"> /\w{,2}/.match('xyz') # => #<MatchData "xy"> -
{min,max}— соответствует не менее min и не более max раз:/\w{1,2}/.match('') # => nil /\w{1,2}/.match('x') # => #<MatchData "x"> /\w{1,2}/.match('xyz') # => #<MatchData "xy">
Жадное, ленивое и притяжательное сопоставление
Сопоставление с квантификатором может быть жадным, ленивым или притяжательным:
-
При жадном сопоставлении совпадает максимально возможное число повторений, при котором общее совпадение всё ещё успешно. Жадные квантификаторы:
*,+,?,{min, max}и их варианты. -
При ленивом сопоставлении совпадает минимально необходимое число повторений. Ленивые квантификаторы:
*?,+?,??,{min, max}?и их варианты. -
При притяжательном сопоставлении после обнаружения совпадения возврат не выполняется; совпадение сохраняется, даже если это мешает успешному сопоставлению всего шаблона. Притяжательные квантификаторы:
*+,++,?+. Обратите внимание, что{min, max}и его варианты не поддерживают притяжательное сопоставление.
Дополнительные сведения:
-
О жадном и ленивом сопоставлении см. Выбор минимального или максимального числа повторений.
-
О притяжательном сопоставлении см. Устранение ненужного возврата.
Группы и захваты
Простой regexp имеет (не более) одного совпадения:
re = /\d\d\d\d-\d\d-\d\d/
re.match('1943-02-04') # => #<MatchData "1943-02-04">
re.match('1943-02-04').size # => 1
re.match('foo') # => nil
Добавление одной или нескольких пар скобок (subexpression) задаёт группы, что может привести к появлению нескольких совпавших подстрок, называемых захватами:
re = /(\d\d\d\d)-(\d\d)-(\d\d)/
re.match('1943-02-04') # => #<MatchData "1943-02-04" 1:"1943" 2:"02" 3:"04">
re.match('1943-02-04').size # => 4
Первый захват — это вся совпавшая строка; остальные захваты — это совпавшие подстроки из групп.
У группы может быть квантификатор:
re = /July 4(th)?/
re.match('July 4') # => #<MatchData "July 4" 1:nil>
re.match('July 4th') # => #<MatchData "July 4th" 1:"th">
re = /(foo)*/
re.match('') # => #<MatchData "" 1:nil>
re.match('foo') # => #<MatchData "foo" 1:"foo">
re.match('foofoo') # => #<MatchData "foofoo" 1:"foo">
re = /(foo)+/
re.match('') # => nil
re.match('foo') # => #<MatchData "foo" 1:"foo">
re.match('foofoo') # => #<MatchData "foofoo" 1:"foo">
Объект MatchData предоставляет доступ к совпавшим подстрокам:
re = /(\d\d\d\d)-(\d\d)-(\d\d)/
md = re.match('1943-02-04')
# => #<MatchData "1943-02-04" 1:"1943" 2:"02" 3:"04">
md[0] # => "1943-02-04"
md[1] # => "1943"
md[2] # => "02"
md[3] # => "04"
Незахватывающие группы
Группу можно сделать незахватывающей; она остаётся группой (и, например, может иметь квантификатор), но её совпавшая подстрока не включается в захваты.
Незахватывающая группа начинается с ?: (внутри скобок):
# Don't capture the year.
re = /(?:\d\d\d\d)-(\d\d)-(\d\d)/
md = re.match('1943-02-04') # => #<MatchData "1943-02-04" 1:"02" 2:"04">
Обратные ссылки
На совпадение группы также можно ссылаться непосредственно в regexp; такая ссылка называется backreference:
/[csh](..) [csh]\1 in/.match('The cat sat in the hat')
# => #<MatchData "cat sat in" 1:"at">
В этой таблице показано, как каждое подвыражение приведённого выше regexp соответствует подстроке целевой строки:
| Subexpression in Regexp | Matching Substring in Target String | |---------------------------|-------------------------------------| | First '[csh]' | Character 'c' | | '(..)' | First substring 'at' | | First space ' ' | First space character ' ' | | Second '[csh]' | Character 's' | | '\1' (backreference 'at') | Second substring 'at' | | ' in' | Substring ' in' |
Regexp может содержать любое количество групп:
-
Для большого числа групп:
-
Обычная запись
\nприменяется только для n в диапазоне (1..9). -
Запись
MatchData[n]применяется для любого неотрицательного n.
-
-
\0— специальная обратная ссылка на всю совпавшую строку; её нельзя использовать внутри самого regexp, но можно использовать за его пределами (например, при вызове метода подстановки):'The cat sat in the hat'.gsub(/[csh]at/, '\0s') # => "The cats sats in the hats"
Именованные захваты
Как было показано выше, на захват можно сослаться по его номеру. Захвату также можно присвоить имя, добавив префикс ?<name> или ?'name'; это имя (в виде символа) можно использовать как индекс в MatchData[]:
md = /\$(?<dollars>\d+)\.(?'cents'\d+)/.match("$3.67")
# => #<MatchData "$3.67" dollars:"3" cents:"67">
md[:dollars] # => "3"
md[:cents] # => "67"
# The capture numbers are still valid.
md[2] # => "67"
Если regexp содержит именованный захват, безымянных захватов нет:
/\$(?<dollars>\d+)\.(\d+)/.match("$3.67")
# => #<MatchData "$3.67" dollars:"3">
На именованную группу можно сослаться как \k<name>:
/(?<vowel>[aeiou]).\k<vowel>.\k<vowel>/.match('ototomy')
# => #<MatchData "ototo" vowel:"o">
Если (и только если) regexp содержит группы с именованными захватами и расположен перед оператором =~, захваченные подстроки присваиваются локальным переменным с соответствующими именами:
/\$(?<dollars>\d+)\.(?<cents>\d+)/ =~ '$3.67' dollars # => "3" cents # => "67"
Метод Regexp#named_captures возвращает хеш имён захватов и подстрок; метод Regexp#names возвращает массив имён захватов.
Атомарная группировка
Группу можно сделать атомарной с помощью (?>подвыражения).
Это приводит к тому, что подвыражение сопоставляется независимо от остальной части выражения, поэтому совпавшая подстрока фиксируется до конца сопоставления, если только от всего подвыражения не приходится отказаться и вернуться к нему позднее.
Таким образом, подвыражение рассматривается как неделимое целое. Атомарная группировка обычно используется для оптимизации шаблонов и предотвращения ненужного возврата.
Пример без атомарной группировки:
/".*"/.match('"Quote"') # => #<MatchData "\"Quote\"">
Разбор:
-
Начальное подвыражение
"в шаблоне соответствует первому символу"целевой строки. -
Следующее подвыражение
.*соответствует следующей подстрокеQuote“(включая завершающую двойную кавычку). -
Теперь в целевой строке не осталось символов, соответствующих завершающему подвыражению
"в шаблоне; из-за этого общее совпадение не будет найдено. -
Выполняется возврат на одну позицию в совпавшей подстроке:
Quote. -
Теперь последнее подвыражение
"соответствует последней подстроке", и общее совпадение успешно.
Если подвыражение .* сгруппировано атомарно, возврат отключён и общее совпадение не будет найдено:
/"(?>.*)"/.match('"Quote"') # => nil
Атомарная группировка может влиять на производительность; см. Атомарная группа.
Вызовы подвыражений
Как было показано выше, обратная ссылка по номеру (\n) или имени (\k<name>) предоставляет доступ к захваченной подстроке; к соответствующему подвыражению regexp также можно обратиться по номеру (\gn) или имени (\g<name>):
/\A(?<paren>\(\g<paren>*\))*\z/.match('(())')
# ^1
# ^2
# ^3
# ^4
# ^5
# ^6
# ^7
# ^8
# ^9
# ^10
Шаблон:
-
Соответствует началу строки, то есть позиции перед первым символом.
-
Входит в именованную группу
paren. -
Соответствует первому символу строки,
'('. -
Снова вызывает группу
paren, то есть рекурсивно возвращается ко второму шагу. -
Повторно входит в группу
paren. -
Соответствует второму символу строки,
'('. -
Пытается вызвать
parenв третий раз, но безуспешно, поскольку это помешало бы успешному сопоставлению всего шаблона. -
Соответствует третьему символу строки,
')'; отмечает конец второго рекурсивного вызова. -
Соответствует четвёртому символу строки,
')'. -
Соответствует концу строки.
См. Вызовы подвыражений.
Условные конструкции
Условная конструкция имеет вид (?(cond)yes|no), где:
-
cond может быть номером или именем захвата.
-
Если cond захвачен, применяется совпадение yes; в противном случае применяется совпадение no.
-
Если не требуется,
|noможно опустить.
Примеры:
re = /\A(foo)?(?(1)(T)|(F))\z/
re.match('fooT') # => #<MatchData "fooT" 1:"foo" 2:"T" 3:nil>
re.match('F') # => #<MatchData "F" 1:nil 2:nil 3:"F">
re.match('fooF') # => nil
re.match('T') # => nil
re = /\A(?<xyzzy>foo)?(?(<xyzzy>)(T)|(F))\z/
re.match('fooT') # => #<MatchData "fooT" xyzzy:"foo">
re.match('F') # => #<MatchData "F" xyzzy:nil>
re.match('fooF') # => nil
re.match('T') # => nil
Оператор отсутствия
Оператор отсутствия — это специальная группа, которая соответствует всему, что не соответствует содержащимся в ней подвыражениям.
/(?~real)/.match('surrealist') # => #<MatchData "surrea">
/(?~real)ist/.match('surrealist') # => #<MatchData "ealist">
/sur(?~real)ist/.match('surrealist') # => nil
Юникод
Свойства Юникода
Конструкция /\p{property_name}/ (со строчной буквой p) сопоставляет символы по имени свойства Юникода, подобно классу символов; свойство Alpha задаёт буквенные символы:
/\p{Alpha}/.match('a') # => #<MatchData "a">
/\p{Alpha}/.match('1') # => nil
Свойство можно инвертировать, добавив перед его именем символ циркумфлекса (^):
/\p{^Alpha}/.match('1') # => #<MatchData "1">
/\p{^Alpha}/.match('a') # => nil
Или используя \P (заглавную букву P):
/\P{Alpha}/.match('1') # => #<MatchData "1">
/\P{Alpha}/.match('a') # => nil
См. Свойства Юникода, чтобы узнать о regexp на основе многочисленных свойств.
Некоторые часто используемые свойства соответствуют выражениям POSIX в квадратных скобках:
-
/\p{Alnum}/: буквенный или числовой символ -
/\p{Alpha}/: буквенный символ -
/\p{Blank}/: пробел или табуляция -
/\p{Cntrl}/: управляющий символ -
/\p{Digit}/: цифры и подобные символы) -
/\p{Lower}/: строчный буквенный символ -
/\p{Print}/: как\p{Graph}, но включает символ пробела -
/\p{Punct}/: знак пунктуации -
/\p{Space}/: пробельный символ ([:blank:], символ новой строки, возврат каретки и т. д.) -
/\p{Upper}/: заглавный буквенный символ -
/\p{XDigit}/: цифра, допустимая в шестнадцатеричном числе (то есть 0-9a-fA-F)
Также часто используются следующие свойства:
-
/\p{Emoji}/: эмодзи Юникода. -
/\p{Graph}/: символы, за исключением/\p{Cntrl}/и/\p{Space}/. Обратите внимание, что невидимые символы категории Юникода «Формат» включены. -
/\p{Word}/: символ, принадлежащий одной из следующих категорий Юникода (см. ниже) или имеющий одно из следующих свойств Юникода:-
Категории Юникода:
-
Mark(M). -
Decimal Number(Nd) -
Connector Punctuation(Pc).
-
-
Свойства Юникода:
-
Alpha -
Join_Control
-
-
-
/\p{ASCII}/: символ из набора символов ASCII. -
/\p{Any}/: любой символ Юникода (включая неназначенные символы). -
/\p{Assigned}/: назначенный символ.
Категории символов Юникода
Имя категории символов Юникода:
-
Может быть полным или сокращённым.
-
Не зависит от регистра.
-
Считает пробел, дефис и символ подчёркивания эквивалентными.
Примеры:
/\p{lu}/ # => /\p{lu}/
/\p{LU}/ # => /\p{LU}/
/\p{Uppercase Letter}/ # => /\p{Uppercase Letter}/
/\p{Uppercase_Letter}/ # => /\p{Uppercase_Letter}/
/\p{UPPERCASE-LETTER}/ # => /\p{UPPERCASE-LETTER}/
Ниже приведены сокращения и названия категорий символов Юникода. Списки символов каждой категории доступны по ссылкам.
Буквы:
-
L,Letter:LC,LmилиLo. -
LC,Cased_Letter:Ll,LtилиLu.
Знаки:
-
M,Mark:Mc,MeилиMn.
Числа:
-
N,Number:Nd,NlилиNo.
Пунктуация:
-
P,Punctuation:Pc,Pd,Pe,Pf,Pi,PoилиPs. -
S,Symbol:Sc,Sk,SmилиSo. -
Z,Separator:Zl,ZpилиZs. -
C,Other:Cc,Cf,Cn,CoилиCs.
Скрипты и блоки Unicode
К свойствам Unicode относятся:
Выражения POSIX в квадратных скобках
Выражение в квадратных скобках POSIX также похоже на класс символов. Эти выражения представляют собой переносимую альтернативу описанным выше и дополнительно охватывают символы, отличные от ASCII:
-
/\d/соответствует только десятичным цифрам ASCII от0до9. -
/[[:digit:]]/соответствует любому символу категории UnicodeDecimal Number(Nd); см. ниже.
Выражения POSIX в квадратных скобках:
-
/[[:digit:]]/: соответствует цифре Unicode:/[[:digit:]]/.match('9') # => #<MatchData "9"> /[[:digit:]]/.match("\u1fbf9") # => #<MatchData "9"> -
/[[:xdigit:]]/: соответствует цифре, допустимой в шестнадцатеричном числе; эквивалентно[0-9a-fA-F]. -
/[[:upper:]]/: соответствует заглавной букве Unicode:/[[:upper:]]/.match('A') # => #<MatchData "A"> /[[:upper:]]/.match("\u00c6") # => #<MatchData "Æ"> -
/[[:lower:]]/: соответствует строчной букве Unicode:/[[:lower:]]/.match('a') # => #<MatchData "a"> /[[:lower:]]/.match("\u01fd") # => #<MatchData "ǽ"> -
/[[:alpha:]]/: соответствует/[[:upper:]]/или/[[:lower:]]/. -
/[[:alnum:]]/: соответствует/[[:alpha:]]/или/[[:digit:]]/. -
/[[:space:]]/: соответствует пробельному символу Unicode:/[[:space:]]/.match(' ') # => #<MatchData " "> /[[:space:]]/.match("\u2005") # => #<MatchData " "> -
/[[:blank:]]/: соответствует/[[:space:]]/или символу табуляции:/[[:blank:]]/.match(' ') # => #<MatchData " "> /[[:blank:]]/.match("\u2005") # => #<MatchData " "> /[[:blank:]]/.match("\t") # => #<MatchData "\t"> -
/[[:cntrl:]]/: соответствует управляющему символу Unicode:/[[:cntrl:]]/.match("\u0000") # => #<MatchData "\u0000"> /[[:cntrl:]]/.match("\u009f") # => #<MatchData "\u009F"> -
/[[:graph:]]/: соответствует любому символу, кроме/[[:space:]]/или/[[:cntrl:]]/. -
/[[:print:]]/: соответствует/[[:graph:]]/или пробелу. -
/[[:punct:]]/: соответствует любому символу (знак пунктуации Unicode}[www.compart.com/en/unicode/category/Po]:
Ruby также поддерживает следующие выражения в квадратных скобках (не относящиеся к POSIX):
-
/[[:ascii:]]/: соответствует символу из набора символов ASCII. -
/[[:word:]]/: соответствует символу одной из следующих категорий символов Unicode или обладающему одним из следующих свойств Unicode:-
Категории Unicode:
-
Mark(M). -
Decimal Number(Nd) -
Connector Punctuation(Pc).
-
-
Свойства Unicode:
-
Alpha -
Join_Control
-
-
Комментарии
В шаблон регулярного выражения можно включить комментарий с помощью конструкции (?#comment), где comment — подстрока, которую следует игнорировать. произвольный текст, игнорируемый механизмом регулярных выражений:
/foo(?#Ignore me)bar/.match('foobar') # => #<MatchData "foobar">
Комментарий не может содержать неэкранированный символ-терминатор.
См. также Расширенный режим.
Режимы
Каждый из этих модификаторов задаёт режим регулярного выражения:
-
i:/pattern/iвключает режим без учёта регистра. -
m:/pattern/mвключает многострочный режим. -
x:/pattern/xвключает расширенный режим. -
o:/pattern/oвключает режим интерполяции.
Можно применить любой из этих модификаторов, все или ни одного.
Модификаторы i, m и x можно применять к подвыражениям:
-
(?modifier)включает режим для последующих подвыражений -
(?-modifier)отключает режим для последующих подвыражений -
(?modifier:subexp)включает режим для subexp внутри группы -
(?-modifier:subexp)отключает режим для subexp внутри группы
Пример:
re = /(?i)te(?-i)st/
re.match('test') # => #<MatchData "test">
re.match('TEst') # => #<MatchData "TEst">
re.match('TEST') # => nil
re.match('teST') # => nil
re = /t(?i:e)st/
re.match('test') # => #<MatchData "test">
re.match('tEst') # => #<MatchData "tEst">
re.match('tEST') # => nil
Метод Regexp#options возвращает целое число, значение которого показывает настройки режима без учёта регистра, многострочного режима и расширенного режима.
Режим без учёта регистра
По умолчанию регулярное выражение чувствительно к регистру:
/foo/.match('FOO') # => nil
Модификатор i включает режим без учёта регистра:
/foo/i.match('FOO')
# => #<MatchData "FOO">
Метод Regexp#casefold? возвращает значение, указывающее, включён ли режим без учёта регистра.
Многострочный режим
Многострочный режим в Ruby — это то, что обычно называют режимом «точка соответствует всему»:
-
Без модификатора
mподвыражение.не соответствует символам новой строки:/a.c/.match("a\nc") # => nil -
С модификатором оно им соответствует:
/a.c/m.match("a\nc") # => #<MatchData "a\nc">
В отличие от других языков, модификатор m не влияет на якоря ^ и $. В Ruby эти якоря всегда соответствуют границам строк.
Расширенный режим
Модификатор x включает расширенный режим, в котором:
-
Пробельные символы, буквально указанные в шаблоне, игнорируются.
-
Символ
#помечает оставшуюся часть строки, в которой он находится, как комментарий; она также игнорируется при поиске совпадений.
В расширенном режиме пробелы и комментарии можно использовать для создания самодокументируемого регулярного выражения.
Regexp без расширенного режима (соответствует некоторым римским цифрам):
pattern = '^M{0,3}(CM|CD|D?C{0,3})(XC|XL|L?X{0,3})(IX|IV|V?I{0,3})$'
re = /#{pattern}/
re.match('MCMXLIII') # => #<MatchData "MCMXLIII" 1:"CM" 2:"XL" 3:"III">
Regexp в расширенном режиме:
pattern = <<-EOT
^ # beginning of string
M{0,3} # thousands - 0 to 3 Ms
(CM|CD|D?C{0,3}) # hundreds - 900 (CM), 400 (CD), 0-300 (0 to 3 Cs),
# or 500-800 (D, followed by 0 to 3 Cs)
(XC|XL|L?X{0,3}) # tens - 90 (XC), 40 (XL), 0-30 (0 to 3 Xs),
# or 50-80 (L, followed by 0 to 3 Xs)
(IX|IV|V?I{0,3}) # ones - 9 (IX), 4 (IV), 0-3 (0 to 3 Is),
# or 5-8 (V, followed by 0 to 3 Is)
$ # end of string
EOT
re = /#{pattern}/x
re.match('MCMXLIII') # => #<MatchData "MCMXLIII" 1:"CM" 2:"XL" 3:"III">
Режим интерполяции
Модификатор o означает, что при первом обнаружении литерала регулярного выражения с интерполяциями созданный объект Regexp сохраняется и используется при всех последующих вычислениях этого литерала. Без модификатора o созданный объект Regexp не сохраняется, поэтому при каждом вычислении литерала регулярного выражения создаётся новый объект Regexp.
Без модификатора o:
def letters; sleep 5; /[A-Z][a-z]/; end
words = %w[abc def xyz]
start = Time.now
words.each {|word| word.match(/\A[#{letters}]+\z/) }
Time.now - start # => 15.0174892
С модификатором o:
start = Time.now
words.each {|word| word.match(/\A[#{letters}]+\z/o) }
Time.now - start # => 5.0010866
Обратите внимание: если литерал регулярного выражения не содержит интерполяций, поведение o используется по умолчанию.
Кодировки
По умолчанию регулярное выражение, содержащее только символы US-ASCII, имеет кодировку US-ASCII:
re = /foo/ re.source.encoding # => #<Encoding:US-ASCII> re.encoding # => #<Encoding:US-ASCII>
Считается, что регулярное выражение, содержащее символы, отличные от US-ASCII, использует исходную кодировку. Это можно переопределить одним из следующих модификаторов.
-
/pat/n: US-ASCII, если содержит только символы US-ASCII, в противном случае ASCII-8BIT:/foo/n.encoding # => #<Encoding:US-ASCII> /foo\xff/n.encoding # => #<Encoding:ASCII-8BIT> /foo\x7f/n.encoding # => #<Encoding:US-ASCII>
-
/pat/u: UTF-8/foo/u.encoding # => #<Encoding:UTF-8>
-
/pat/e: EUC-JP/foo/e.encoding # => #<Encoding:EUC-JP>
-
/pat/s: Windows-31J/foo/s.encoding # => #<Encoding:Windows-31J>
Регулярное выражение можно сопоставить с целевой строкой, если выполняется одно из условий:
-
У них одинаковая кодировка.
-
Кодировка регулярного выражения является фиксированной, а строка содержит только символы ASCII. Метод
Regexp#fixed_encoding?возвращает значение, указывающее, имеет ли регулярное выражение фиксированную кодировку.
При попытке сопоставить несовместимые кодировки возникает исключение Encoding::CompatibilityError.
Пример:
re = eval("# encoding: ISO-8859-1\n/foo\\xff?/")
re.encoding # => #<Encoding:ISO-8859-1>
re =~ "foo".encode("UTF-8") # => 0
re =~ "foo\u0100" # Raises Encoding::CompatibilityError
Кодировку можно явно зафиксировать, указав Regexp::FIXEDENCODING во втором аргументе метода Regexp.new:
# Regexp with encoding ISO-8859-1.
re = Regexp.new("a".force_encoding('iso-8859-1'), Regexp::FIXEDENCODING)
re.encoding # => #<Encoding:ISO-8859-1>
# Target string with encoding UTF-8.
s = "a\u3042"
s.encoding # => #<Encoding:UTF-8>
re.match(s) # Raises Encoding::CompatibilityError.
Тайм-ауты
Если источник регулярного выражения или целевая строка поступают из ненадёжного источника, вредоносные данные могут привести к атаке типа «отказ в обслуживании»; чтобы предотвратить такую атаку, рекомендуется установить тайм-аут.
У Regexp есть два значения тайм-аута:
-
Тайм-аут класса по умолчанию, используемый для регулярного выражения, тайм-аут экземпляра которого равен
nil; изначально он равенnil, и его можно установить методомRegexp.timeout=:Regexp.timeout # => nil Regexp.timeout = 3.0 Regexp.timeout # => 3.0
-
Тайм-аут экземпляра, значение которого по умолчанию равно
nilи которое можно установить вRegexp.new:re = Regexp.new('foo', timeout: 5.0) re.timeout # => 5.0
Если regexp.timeout равен nil, тайм-аут «передаётся дальше» в Regexp.timeout; если regexp.timeout не равен nil, именно это значение определяет срабатывание тайм-аута:
| regexp.timeout Value | Regexp.timeout Value | Result | |----------------------|----------------------|-----------------------------| | nil | nil | Never times out. | | nil | Float | Times out in Float seconds. | | Float | Any | Times out in Float seconds. |
Оптимизация
Для некоторых значений шаблона и целевой строки время поиска совпадений может расти полиномиально или экспоненциально относительно размера входных данных; связанная с этим потенциальная уязвимость называется атакой типа отказ в обслуживании с помощью регулярных выражений (ReDoS).
Для защиты от атак ReDoS при сопоставлении регулярных выражений может применяться оптимизация. При её использовании время сопоставления растёт линейно (а не полиномиально или экспоненциально) относительно размера входных данных, поэтому атака ReDoS невозможна.
Эта оптимизация применяется, если шаблон соответствует следующим критериям:
-
Нет обратных ссылок.
-
Нет вызовов подвыражений.
-
Нет вложенных якорей просмотра вперёд или назад и атомарных групп.
-
Нет вложенных квантификаторов с подсчётом (то есть вложенных квантификаторов в стиле
{n},{min,},{,max}или{min,max})
Можно использовать метод Regexp.linear_time?, чтобы определить, соответствует ли шаблон этим критериям:
Regexp.linear_time?(/a*/) # => true
Regexp.linear_time?('a*') # => true
Regexp.linear_time?(/(a*)\1/) # => false
Однако источник может быть ненадёжным, даже если метод возвращает true, поскольку оптимизация использует мемоизацию (которая может потребовать большого объёма памяти).
Ссылки
Читайте (электронные PDF-книги):
-
Искусство регулярных выражений, автор Джеффри Э. Ф. Фридл.
-
Сборник рецептов по регулярным выражениям, авторы Ян Гойвартс и Стивен Левитан.
Изучайте и тестируйте (интерактивный онлайн-редактор):
Константы
- EXTENDED
-
см.
Regexp.optionsиRegexp.new - FIXEDENCODING
-
см.
Regexp.optionsиRegexp.new - IGNORECASE
-
см.
Regexp.optionsиRegexp.new - MULTILINE
-
см.
Regexp.optionsиRegexp.new - NOENCODING
-
см.
Regexp.optionsиRegexp.new
Открытые методы класса
Псевдоним для Regexp.new
static VALUE
rb_reg_s_quote(VALUE c, VALUE str)
{
return rb_reg_quote(reg_operand(str, TRUE));
} Возвращает новую строку, в которой экранированы все символы, имеющие специальное значение в регулярном выражении:
s = Regexp.escape('\*?{}.') # => "\\\\\\*\\?\\{\\}\\."
Для любой строки s этот вызов возвращает объект MatchData:
r = Regexp.new(Regexp.escape(s)) # => /\\\\\\\*\\\?\\\{\\\}\\\./
r.match(s) # => #<MatchData "\\\\\\*\\?\\{\\}\\.">
# File ext/json/lib/json/add/regexp.rb, line 9 def self.json_create(object) new(object['s'], object['o']) end
См. as_json.
static VALUE
rb_reg_s_last_match(int argc, VALUE *argv, VALUE _)
{
if (rb_check_arity(argc, 0, 1) == 1) {
VALUE match = rb_backref_get();
int n;
if (NIL_P(match)) return Qnil;
n = match_backref_number(match, argv[0]);
return rb_reg_nth_match(n, match);
}
return match_getter();
} Без аргумента возвращает значение $~, то есть результат последнего сопоставления с шаблоном (см. глобальные переменные Regexp):
/c(.)t/ =~ 'cat' # => 0 Regexp.last_match # => #<MatchData "cat" 1:"a"> /a/ =~ 'foo' # => nil Regexp.last_match # => nil
При неотрицательном целочисленном аргументе n возвращает поле _n_ в matchdata, если оно есть, либо nil, если его нет:
/c(.)t/ =~ 'cat' # => 0 Regexp.last_match(0) # => "cat" Regexp.last_match(1) # => "a" Regexp.last_match(2) # => nil
При отрицательном целочисленном аргументе n отсчёт ведётся назад от последнего поля:
Regexp.last_match(-1) # => "a"
При строковом аргументе или аргументе-символе name возвращает строковое значение именованной группы захвата, если она есть:
/(?<lhs>\w+)\s*=\s*(?<rhs>\w+)/ =~ 'var = val'
Regexp.last_match # => #<MatchData "var = val" lhs:"var"rhs:"val">
Regexp.last_match(:lhs) # => "var"
Regexp.last_match('rhs') # => "val"
Regexp.last_match('foo') # Raises IndexError.
static VALUE
rb_reg_s_linear_time_p(int argc, VALUE *argv, VALUE self)
{
struct reg_init_args args;
VALUE re = reg_extract_args(argc, argv, &args);
if (NIL_P(re)) {
re = reg_init_args(rb_reg_alloc(), args.str, args.enc, args.flags);
}
return RBOOL(onig_check_linear_time(RREGEXP_PTR(re)));
} Возвращает true, если сопоставление с re может быть выполнено за линейное время относительно длины входной строки.
Regexp.linear_time?(/re/) # => true
Обратите внимание, что это свойство интерпретатора Ruby, а не переданного регулярного выражения. Одно и то же регулярное выражение может выполняться за линейное время или нет в зависимости от используемого двоичного файла Ruby. Обратная и прямая совместимость возвращаемого значения этого метода не гарантируются. Текущий алгоритм — (*1), но в будущем он может измениться. Альтернативные реализации также могут вести себя иначе. Например, они могут всегда возвращать false.
static VALUE
rb_reg_initialize_m(int argc, VALUE *argv, VALUE self)
{
struct reg_init_args args;
VALUE re = reg_extract_args(argc, argv, &args);
if (NIL_P(re)) {
reg_init_args(self, args.str, args.enc, args.flags);
}
else {
reg_copy(self, re);
}
set_timeout(&RREGEXP_PTR(self)->timelimit, args.timeout);
return self;
} Если указан аргумент string, возвращает новое регулярное выражение с заданной строкой и параметрами:
r = Regexp.new('foo') # => /foo/
r.source # => "foo"
r.options # => 0
Необязательный аргумент options может быть одним из следующих значений:
-
Stringс параметрами:Regexp.new('foo', 'i') # => /foo/i Regexp.new('foo', 'im') # => /foo/im -
Побитовое ИЛИ одной или нескольких констант
Regexp::EXTENDED,Regexp::IGNORECASE,Regexp::MULTILINEиRegexp::NOENCODING:Regexp.new('foo', Regexp::IGNORECASE) # => /foo/i Regexp.new('foo', Regexp::EXTENDED) # => /foo/x Regexp.new('foo', Regexp::MULTILINE) # => /foo/m Regexp.new('foo', Regexp::NOENCODING) # => /foo/n flags = Regexp::IGNORECASE | Regexp::EXTENDED | Regexp::MULTILINE Regexp.new('foo', flags) # => /foo/mix -
nilилиfalse; это значение игнорируется. -
Любое другое истинное значение; в этом случае регулярное выражение будет нечувствительно к регистру.
Если указан необязательный ключевой аргумент timeout, его значение типа float переопределяет интервал тайм-аута для класса, Regexp.timeout. Если в качестве +timeout передано nil, используется интервал тайм-аута для класса, Regexp.timeout.
Если указан аргумент regexp, возвращает новое регулярное выражение. Исходная строка, параметры и тайм-аут совпадают с regexp. Аргументы options и n_flag не влияют на результат. Тайм-аут можно переопределить ключевым аргументом timeout.
options = Regexp::MULTILINE
r = Regexp.new('foo', options, timeout: 1.1) # => /foo/m
r2 = Regexp.new(r) # => /foo/m
r2.timeout # => 1.1
r3 = Regexp.new(r, timeout: 3.14) # => /foo/m
r3.timeout # => 3.14
static VALUE
rb_reg_s_quote(VALUE c, VALUE str)
{
return rb_reg_quote(reg_operand(str, TRUE));
} Возвращает новую строку, в которой экранированы все символы, имеющие специальное значение в регулярном выражении:
s = Regexp.escape('\*?{}.') # => "\\\\\\*\\?\\{\\}\\."
Для любой строки s этот вызов возвращает объект MatchData:
r = Regexp.new(Regexp.escape(s)) # => /\\\\\\\*\\\?\\\{\\\}\\\./
r.match(s) # => #<MatchData "\\\\\\*\\?\\{\\}\\.">
static VALUE
rb_reg_s_timeout_get(VALUE dummy)
{
double d = hrtime2double(rb_reg_match_time_limit);
if (d == 0.0) return Qnil;
return DBL2NUM(d);
} Возвращает текущий интервал тайм-аута по умолчанию для сопоставления с Regexp в секундах. nil означает, что тайм-аут по умолчанию не настроен.
static VALUE
rb_reg_s_timeout_set(VALUE dummy, VALUE timeout)
{
rb_ractor_ensure_main_ractor("can not access Regexp.timeout from non-main Ractors");
set_timeout(&rb_reg_match_time_limit, timeout);
return timeout;
} Задаёт интервал тайм-аута по умолчанию для сопоставления с Regexp в секундах. nil означает, что тайм-аут по умолчанию не настроен. Эта настройка действует на уровне процесса. Чтобы задать тайм-аут для каждого объекта Regexp отдельно, используйте ключевой аргумент timeout метода Regexp.new.
Regexp.timeout = 1 /^a*b?a*$/ =~ "a" * 100000 + "x" #=> regexp match timeout (RuntimeError)
static VALUE
rb_reg_s_try_convert(VALUE dummy, VALUE re)
{
return rb_check_regexp_type(re);
} Возвращает object, если это регулярное выражение:
Regexp.try_convert(/re/) # => /re/
В противном случае, если object отвечает на вызов :to_regexp, вызывает object.to_regexp и возвращает результат.
Возвращает nil, если object не отвечает на вызов :to_regexp.
Regexp.try_convert('re') # => nil
Вызывает исключение, если object.to_regexp не возвращает регулярное выражение.
static VALUE
rb_reg_s_union_m(VALUE self, VALUE args)
{
VALUE v;
if (RARRAY_LEN(args) == 1 &&
!NIL_P(v = rb_check_array_type(rb_ary_entry(args, 0)))) {
return rb_reg_s_union(self, v);
}
return rb_reg_s_union(self, args);
} Возвращает новое регулярное выражение, представляющее объединение заданных шаблонов:
r = Regexp.union(%w[cat dog]) # => /cat|dog/
r.match('cat') # => #<MatchData "cat">
r.match('dog') # => #<MatchData "dog">
r.match('cog') # => nil
Для каждого шаблона, заданного строкой, используется Regexp.new(pattern):
Regexp.union('penzance') # => /penzance/
Regexp.union('a+b*c') # => /a\+b\*c/
Regexp.union('skiing', 'sledding') # => /skiing|sledding/
Regexp.union(['skiing', 'sledding']) # => /skiing|sledding/
Каждый шаблон, заданный регулярным выражением, используется без изменений, включая его флаги:
Regexp.union(/foo/i, /bar/m, /baz/x) # => /(?i-mx:foo)|(?m-ix:bar)|(?x-mi:baz)/ Regexp.union([/foo/i, /bar/m, /baz/x]) # => /(?i-mx:foo)|(?m-ix:bar)|(?x-mi:baz)/
Без аргументов возвращает /(?!)/:
Regexp.union # => /(?!)/
Если какой-либо шаблон регулярного выражения содержит группы захвата, поведение не определено.
Открытые методы экземпляра
Возвращает true, если object — другое регулярное выражение Regexp с тем же шаблоном, флагами и кодировкой, что и self, и false в противном случае:
/foo/ == Regexp.new('foo') # => true
/foo/ == /foo/i # => false
/foo/ == Regexp.new('food') # => false
/foo/ == Regexp.new("abc".force_encoding("euc-jp")) # => false
static VALUE
rb_reg_eqq(VALUE re, VALUE str)
{
long start;
str = reg_operand(str, FALSE);
if (NIL_P(str)) {
rb_backref_set(Qnil);
return Qfalse;
}
start = rb_reg_search(re, str, 0, 0);
return RBOOL(start >= 0);
} Возвращает true, если self находит совпадение в string:
/^[a-z]*$/ === 'HELLO' # => false /^[A-Z]*$/ === 'HELLO' # => true
Этот метод вызывается в операторах case:
s = 'HELLO' case s when /\A[a-z]*\z/; print "Lower case\n" when /\A[A-Z]*\z/; print "Upper case\n" else print "Mixed case\n" end # => "Upper case"
VALUE
rb_reg_match(VALUE re, VALUE str)
{
long pos = reg_match_pos(re, &str, 0, NULL);
if (pos < 0) return Qnil;
pos = rb_str_sublen(str, pos);
return LONG2FIX(pos);
} Возвращает целочисленный индекс (в символах) первого совпадения для self и string либо nil, если совпадений нет; также устанавливает глобальные переменные Regexp:
/at/ =~ 'input data' # => 7 $~ # => #<MatchData "at"> /ax/ =~ 'input data' # => nil $~ # => nil
Присваивает именованные группы захвата локальным переменным с такими же именами только в том случае, если self:
-
Является литералом регулярного выражения; см. литералы регулярных выражений.
-
Не содержит интерполяций; см. интерполяцию регулярных выражений.
-
Находится слева в выражении.
Пример:
/(?<lhs>\w+)\s*=\s*(?<rhs>\w+)/ =~ ' x = y ' p lhs # => "x" p rhs # => "y"
Присваивает nil, если совпадение не найдено:
/(?<lhs>\w+)\s*=\s*(?<rhs>\w+)/ =~ ' x = ' p lhs # => nil p rhs # => nil
Не выполняет присваивание локальным переменным, если self не является литералом регулярного выражения:
r = /(?<foo>\w+)\s*=\s*(?<foo>\w+)/ r =~ ' x = y ' p foo # Undefined local variable p bar # Undefined local variable
Присваивание не выполняется, если регулярное выражение находится не слева:
' x = y ' =~ /(?<foo>\w+)\s*=\s*(?<foo>\w+)/ p foo, foo # Undefined local variables
Интерполяция регулярного выражения, #{}, также отменяет присваивание:
r = /(?<foo>\w+)/
/(?<foo>\w+)\s*=\s*#{r}/ =~ 'x = y'
p foo # Undefined local variable
VALUE
rb_reg_match2(VALUE re)
{
long start;
VALUE line = rb_lastline_get();
if (!RB_TYPE_P(line, T_STRING)) {
rb_backref_set(Qnil);
return Qnil;
}
start = rb_reg_search(re, line, 0, 0);
if (start < 0) {
return Qnil;
}
start = rb_str_sublen(line, start);
return LONG2FIX(start);
} Эквивалентно rxp =~ $_:
$_ = "input data" ~ /at/ # => 7
# File ext/json/lib/json/add/regexp.rb, line 28
def as_json(*)
{
JSON.create_id => self.class.name,
'o' => options,
's' => source,
}
end Методы Regexp#as_json и Regexp.json_create можно использовать для сериализации и десериализации объекта Regexp; см. Marshal.
Метод Regexp#as_json сериализует self, возвращая хеш из двух элементов, представляющий self:
require 'json/add/regexp'
x = /foo/.as_json
# => {"json_class"=>"Regexp", "o"=>0, "s"=>"foo"}
Метод JSON.create десериализует такой хеш и возвращает объект Regexp:
Regexp.json_create(x) # => /foo/
static VALUE
rb_reg_casefold_p(VALUE re)
{
rb_reg_check(re);
return RBOOL(RREGEXP_PTR(re)->options & ONIG_OPTION_IGNORECASE);
} Возвращает true, если в self установлен флаг нечувствительности к регистру, и false в противном случае:
/a/.casefold? # => false /a/i.casefold? # => true /(?i:a)/.casefold? # => false
VALUE
rb_obj_encoding(VALUE obj)
{
int idx = rb_enc_get_index(obj);
if (idx < 0) {
rb_raise(rb_eTypeError, "unknown encoding");
}
return rb_enc_from_encoding_index(idx & ENC_INDEX_MASK);
} Возвращает объект Encoding, представляющий кодировку объекта.
static VALUE
rb_reg_fixed_encoding_p(VALUE re)
{
return RBOOL(FL_TEST(re, KCODE_FIXED));
} Возвращает false, если self применимо к строке с любой ASCII-совместимой кодировкой; в противном случае возвращает true:
r = /a/ # => /a/
r.fixed_encoding? # => false
r.match?("\u{6666} a") # => true
r.match?("\xa1\xa2 a".force_encoding("euc-jp")) # => true
r.match?("abc".force_encoding("euc-jp")) # => true
r = /a/u # => /a/
r.fixed_encoding? # => true
r.match?("\u{6666} a") # => true
r.match?("\xa1\xa2".force_encoding("euc-jp")) # Raises exception.
r.match?("abc".force_encoding("euc-jp")) # => true
r = /\u{6666}/ # => /\u{6666}/
r.fixed_encoding? # => true
r.encoding # => #<Encoding:UTF-8>
r.match?("\u{6666} a") # => true
r.match?("\xa1\xa2".force_encoding("euc-jp")) # Raises exception.
r.match?("abc".force_encoding("euc-jp")) # => false
VALUE
rb_reg_hash(VALUE re)
{
st_index_t hashval = reg_hash(re);
return ST2FIX(hashval);
} Возвращает целочисленное хеш-значение для self.
Связанный метод: Object#hash.
static VALUE
rb_reg_inspect(VALUE re)
{
if (!RREGEXP_PTR(re) || !RREGEXP_SRC(re) || !RREGEXP_SRC_PTR(re)) {
return rb_any_to_s(re);
}
return rb_reg_desc(re);
} Возвращает отформатированное строковое представление self:
/ab+c/ix.inspect # => "/ab+c/ix"
Связанный метод: Regexp#to_s.
static VALUE
rb_reg_match_m(int argc, VALUE *argv, VALUE re)
{
VALUE result = Qnil, str, initpos;
long pos;
if (rb_scan_args(argc, argv, "11", &str, &initpos) == 2) {
pos = NUM2LONG(initpos);
}
else {
pos = 0;
}
pos = reg_match_pos(re, &str, pos, &result);
if (pos < 0) {
rb_backref_set(Qnil);
return Qnil;
}
rb_match_busy(result);
if (!NIL_P(result) && rb_block_given_p()) {
return rb_yield(result);
}
return result;
} Если блок не указан, возвращает объект MatchData, описывающий совпадение, если оно есть, либо nil, если совпадений нет; поиск начинается с указанной позиции offset в string:
/abra/.match('abracadabra') # => #<MatchData "abra">
/abra/.match('abracadabra', 4) # => #<MatchData "abra">
/abra/.match('abracadabra', 8) # => nil
/abra/.match('abracadabra', 800) # => nil
string = "\u{5d0 5d1 5e8 5d0}cadabra"
/abra/.match(string, 7) #=> #<MatchData "abra">
/abra/.match(string, 8) #=> nil
/abra/.match(string.b, 8) #=> #<MatchData "abra">
Если блок указан, вызывает его только при обнаружении совпадения; возвращает значение блока:
/abra/.match('abracadabra') {|matchdata| p matchdata }
# => #<MatchData "abra">
/abra/.match('abracadabra', 4) {|matchdata| p matchdata }
# => #<MatchData "abra">
/abra/.match('abracadabra', 8) {|matchdata| p matchdata }
# => nil
/abra/.match('abracadabra', 8) {|marchdata| fail 'Cannot happen' }
# => nil
Вывод (для первых двух блоков выше):
#<MatchData "abra">
#<MatchData "abra">
/(.)(.)(.)/.match("abc")[2] # => "b"
/(.)(.)/.match("abc", 1)[2] # => "c"
static VALUE
rb_reg_match_m_p(int argc, VALUE *argv, VALUE re)
{
long pos = rb_check_arity(argc, 1, 2) > 1 ? NUM2LONG(argv[1]) : 0;
return rb_reg_match_p(re, argv[0], pos);
} Возвращает true или false, указывая, найдено ли совпадение с регулярным выражением, не обновляя $~ и другие связанные переменные. Если указан второй параметр, он задаёт позицию в строке, с которой начинается поиск.
/R.../.match?("Ruby") # => true
/R.../.match?("Ruby", 1) # => false
/P.../.match?("Ruby") # => false
$& # => nil
static VALUE
rb_reg_named_captures(VALUE re)
{
regex_t *reg = (rb_reg_check(re), RREGEXP_PTR(re));
VALUE hash = rb_hash_new_with_size(onig_number_of_names(reg));
onig_foreach_name(reg, reg_named_captures_iter, (void*)hash);
return hash;
} Возвращает хеш, представляющий именованные группы захвата self (см. именованные группы захвата):
-
Каждый ключ — имя именованной группы захвата.
-
Каждое значение — массив целочисленных индексов этой именованной группы захвата.
Примеры:
/(?<foo>.)(?<bar>.)/.named_captures # => {"foo"=>[1], "bar"=>[2]}
/(?<foo>.)(?<foo>.)/.named_captures # => {"foo"=>[1, 2]}
/(.)(.)/.named_captures # => {}
static VALUE
rb_reg_names(VALUE re)
{
VALUE ary;
rb_reg_check(re);
ary = rb_ary_new_capa(onig_number_of_names(RREGEXP_PTR(re)));
onig_foreach_name(RREGEXP_PTR(re), reg_names_iter, (void*)ary);
return ary;
} Возвращает массив имён групп захвата (см. именованные группы захвата):
/(?<foo>.)(?<bar>.)(?<baz>.)/.names # => ["foo", "bar", "baz"] /(?<foo>.)(?<foo>.)/.names # => ["foo"] /(.)(.)/.names # => []
static VALUE
rb_reg_options_m(VALUE re)
{
int options = rb_reg_options(re);
return INT2NUM(options);
} Возвращает целое число, биты которого указывают параметры, установленные в self.
Биты параметров:
Regexp::IGNORECASE # => 1 Regexp::EXTENDED # => 2 Regexp::MULTILINE # => 4
Примеры:
/foo/.options # => 0 /foo/i.options # => 1 /foo/x.options # => 2 /foo/m.options # => 4 /foo/mix.options # => 7
Обратите внимание, что в возвращаемом целом числе могут быть установлены дополнительные биты; они хранятся внутри self, игнорируются при передаче в Regexp.new и могут быть проигнорированы вызывающим кодом:
Возвращает набор битов, соответствующих параметрам, использованным при создании этого регулярного выражения (подробности см. в Regexp::new). Обратите внимание, что в возвращаемых параметрах могут быть установлены дополнительные биты: они используются внутри кода регулярных выражений. Эти дополнительные биты игнорируются, если параметры передаются в Regexp::new:
r = /\xa1\xa2/e # => /\xa1\xa2/ r.source # => "\\xa1\\xa2" r.options # => 16 Regexp.new(r.source, r.options) # => /\xa1\xa2/
static VALUE
rb_reg_source(VALUE re)
{
VALUE str;
rb_reg_check(re);
str = rb_str_dup(RREGEXP_SRC(re));
return str;
} Возвращает исходную строку self:
/ab+c/ix.source # => "ab+c"
Экранирующие последовательности Regexp сохраняются:
/\x20\+/.source # => "\\x20\\+"
Экранирующие символы лексера не сохраняются:
/\//.source # => "/"
static VALUE
rb_reg_timeout_get(VALUE re)
{
rb_reg_check(re);
double d = hrtime2double(RREGEXP_PTR(re)->timelimit);
if (d == 0.0) return Qnil;
return DBL2NUM(d);
} Возвращает интервал тайм-аута для сопоставления с Regexp в секундах. nil означает, что тайм-аут по умолчанию не настроен.
Эта настройка действует для отдельного объекта. Глобальная настройка, заданная с помощью Regexp.timeout=, игнорируется, если задана настройка для конкретного объекта.
re = Regexp.new("^a*b?a*$", timeout: 1)
re.timeout #=> 1.0
re =~ "a" * 100000 + "x" #=> regexp match timeout (RuntimeError)
# File ext/json/lib/json/add/regexp.rb, line 45 def to_json(*args) as_json.to_json(*args) end
Возвращает строку JSON, представляющую self:
require 'json/add/regexp' puts /foo/.to_json
Вывод:
{"json_class":"Regexp","o":0,"s":"foo"}
static VALUE
rb_reg_to_s(VALUE re)
{
return rb_reg_str_with_term(re, '/');
} Возвращает строку, отображающую параметры и строку self:
r0 = /ab+c/ix s0 = r0.to_s # => "(?ix-m:ab+c)"
Возвращённую строку можно использовать как аргумент для Regexp.new или как интерполируемый текст для интерполяции регулярных выражений:
r1 = Regexp.new(s0) # => /(?ix-m:ab+c)/
r2 = /#{s0}/ # => /(?ix-m:ab+c)/
Обратите внимание, что r1 и r2 не равны r0, поскольку исходные строки у них различаются:
r0 == r1 # => false r0.source # => "ab+c" r1.source # => "(?ix-m:ab+c)"
Связанный метод: Regexp#inspect.
Ruby Core © 1993–2025 Yukihiro Matsumoto
Licensed under the Ruby License.
Ruby Standard Library © contributors
Licensed under their own licenses.