класс 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— совсем другое: она возвращает имя выполняемой в данный момент программы.
Эти переменные, за исключением $~, являются сокращёнными обозначениями методов $~. См. раздел Эквивалентность глобальных переменных в MatchData.
Примеры:
# 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">
Шаблон в просмотре назад должен иметь фиксированную ширину. Однако варианты на верхнем уровне могут иметь разную длину. Например, (?<=a|bc) допустимо. (?<=aaa(?:b|cd)) не допускается.
Якорь сброса совпадения
-
\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 "xyz"> -
+— соответствует один или более раз:/\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 — это то, что обычно называют «режимом dot-all»:
-
Без модификатора
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, поскольку в оптимизации используется мемоизация (которая может потребовать большого объёма памяти).
Ссылки
Рекомендуем прочитать:
-
Mastering Regular Expressions Джеффри Э. Ф. Фридла.
-
Regular Expressions Cookbook Яна Гойвертса и Стивена Левитана.
Изучите и протестируйте:
-
Rubular: интерактивный онлайн-редактор.
Константы
- 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, его значение с плавающей точкой переопределяет интервал тайм-аута для класса, 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);
} VALUE
rb_reg_equal(VALUE re1, VALUE re2)
{
if (re1 == re2) return Qtrue;
if (!RB_TYPE_P(re2, T_REGEXP)) return Qfalse;
rb_reg_check(re1); rb_reg_check(re2);
if (FL_TEST(re1, KCODE_FIXED) != FL_TEST(re2, KCODE_FIXED)) return Qfalse;
if (RREGEXP_PTR(re1)->options != RREGEXP_PTR(re2)->options) return Qfalse;
if (RREGEXP_SRC_LEN(re1) != RREGEXP_SRC_LEN(re2)) return Qfalse;
if (ENCODING_GET(re1) != ENCODING_GET(re2)) return Qfalse;
return RBOOL(memcmp(RREGEXP_SRC_PTR(re1), RREGEXP_SRC_PTR(re2), RREGEXP_SRC_LEN(re1)) == 0);
} 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.