класс 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: Литералы 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 "\\.">
Method 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в регулярном выражении, исключается из результата. Например, следующие два регулярных выражения почти эквивалентны:/ab\Kc/.match('abc') # => #<MatchData "c"> /(?<=ab)c/.match('abc') # => #<MatchData "c">Они соответствуют одной и той же строке и
$&равно'c', в то время как позиция совпадения отличается.Как и следующие два регулярных выражения:
/(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 предоставляет удобный способ построения регулярного выражения с альтернативами.
Квантификаторы
Простое регулярное выражение сопоставляет один символ:
/\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}и его варианты не поддерживают притяжательное сопоставление.
Подробнее:
-
О жадном и ленивом поиске см. Выбор минимального или максимального повторения.
-
О притяжательном поиске см. Исключить ненужный откат.
Группы и захват
Простое регулярное выражение имеет (как максимум) одно совпадение:
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">
Обратные ссылки
Совпадение группы также может быть ссылаться внутри самого регулярного выражения; такая ссылка называется backreference:
/[csh](..) [csh]\1 in/.match('The cat sat in the hat')
# => #<MatchData "cat sat in" 1:"at">
В этой таблице показано, как каждое подвыражение в регулярном выражении выше сопоставляет подстроку в целевой строке:
| 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' |
Регулярное выражение может содержать любое количество групп:
-
Для большого числа групп:
-
Обычная
\nнотация применима только для n в диапазоне (1..9). -
MatchData[n]нотация применима для любого неотрицательного n.
-
-
\0— это специальная обратная ссылка, которая ссылается на всю сопоставленную строку; она не может использоваться внутри самого регулярного выражения, но может использоваться вне его (например, в вызове метода подстановки):'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"
Когда регулярное выражение содержит именованные захваты, безымянных захватов нет:
/\$(?<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">
Когда (и только когда) регулярное выражение содержит именованные группы захватов и появляется перед оператором =~, сопоставленные подстроки назначаются локальным переменным с соответствующими именами:
/\$(?<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 может быть номером или именем захвата.
-
Применяемое совпадение — yes, если cond захвачен; в противном случае применяется совпадение — 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
Unicode
Свойства Unicode
Конструкт /\p{property_name}/ (с маленькими p ) соответствует символам с использованием имени свойства Unicode, очень похоже на класс символов; свойство 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
См. Свойства Unicode для regexps, основанных на многочисленных свойствах.
Некоторые часто используемые свойства соответствуют выражениям 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}/: Эмодзи Unicode. -
/\p{Graph}/: Символ, отличный от пробела (исключает пробелы, символы управления и аналогичные). -
/\p{Word}/: Элемент одной из этих категорий символов Unicode (см. ниже) или имеющий одно из этих свойств Unicode:-
Категории Unicode:
-
Mark(M). -
Decimal Number(Nd) -
Connector Punctuation(Pc).
-
-
Свойства Unicode:
-
Alpha -
Join_Control
-
-
-
/\p{ASCII}/: Символ в наборе символов ASCII. -
/\p{Any}/: Любой символ Unicode (включая неназначенные символы). -
/\p{Assigned}/: Назначенный символ.
Категории символов Unicode
Имя категории символа Unicode:
-
Может быть полным именем или сокращенным именем.
-
Регистронезависимо.
-
Рассматривает пробел, дефис и подчеркивание как эквивалентные.
Примеры:
/\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}/
Ниже приведены сокращения и имена категорий символов Unicode. Перечисления символов в каждой категории находятся по ссылкам.
Буквы:
-
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:]]/: Соответствует числу Юникода:/[[:digit:]]/.match('9') # => #<MatchData "9"> /[[:digit:]]/.match("\u1fbf9") # => #<MatchData "9"> -
/[[:xdigit:]]/: Соответствует цифре, разрешённой в шестнадцатеричном числе; эквивалентно[0-9a-fA-F]. -
/[[:upper:]]/: Соответствует заглавной букве Юникода:/[[:upper:]]/.match('A') # => #<MatchData "A"> /[[:upper:]]/.match("\u00c6") # => #<MatchData "Æ"> -
/[[:lower:]]/: Соответствует строчной букве Юникода:/[[:lower:]]/.match('a') # => #<MatchData "a"> /[[:lower:]]/.match("\u01fd") # => #<MatchData "ǽ"> -
/[[:alpha:]]/: Соответствует/[[:upper:]]/или/[[:lower:]]/. -
/[[:alnum:]]/: Соответствует/[[:alpha:]]/или/[[:digit:]]/. -
/[[:space:]]/: Соответствует пробельному символу Юникода:/[[:space:]]/.match(' ') # => #<MatchData " "> /[[:space:]]/.match("\u2005") # => #<MatchData " "> -
/[[:blank:]]/: Соответствует/[[:space:]]/или табуляции:/[[:blank:]]/.match(' ') # => #<MatchData " "> /[[:blank:]]/.match("\u2005") # => #<MatchData " "> /[[:blank:]]/.match("\t") # => #<MatchData "\t"> -
/[[:cntrl:]]/: Соответствует управляющему символу Юникода:/[[:cntrl:]]/.match("\u0000") # => #<MatchData "\u0000"> /[[:cntrl:]]/.match("\u009f") # => #<MatchData "\u009F"> -
/[[:graph:]]/: Соответствует любому символу, кроме/[[:space:]]/или/[[:cntrl:]]/. -
/[[:print:]]/: Соответствует/[[:graph:]]/или пробельному символу. -
/[[:punct:]]/: Соответствует любому символу пунктуации Юникода [www.compart.com/en/unicode/category/Po]:
Ruby также поддерживает эти (не POSIX) выражения в квадратных скобках:
-
/[[:ascii:]]/: Соответствует символу в наборе символов ASCII. -
/[[:word:]]/: Соответствует символу в одной из этих категорий символов Юникода или имеющему одно из этих свойств Юникода:-
Категории Юникода:
-
Mark(M). -
Decimal Number(Nd) -
Connector Punctuation(Pc).
-
-
Свойства Юникода:
-
Alpha -
Join_Control
-
-
Комментарии
Комментарий может быть включён в шаблон regexp с помощью конструкции (?#comment), где comment — подстрока, которая должна быть проигнорирована. произвольный текст, игнорируемый движком regexp:
/foo(?#Ignore me)bar/.match('foobar') # => #<MatchData "foobar">
Комментарий не может включать неэкранированный символ-разделитель.
См. также Расширенный режим.
Режимы
Каждый из этих модификаторов устанавливает режим для regexp:
-
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 возвращает целое число, значение которого показывает настройки для регистронезависимого режима, многострочного режима и расширенного режима.
Регистронезависимый режим
По умолчанию, regexp чувствителен к регистру:
/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.
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 с интерполяциями, сгенерированный объект Regexp сохраняется и используется для всех последующих вычислений этого литерального regexp. Без модификатора o, сгенерированный объект Regexp не сохраняется, поэтому каждое вычисление литерального 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
Обратите внимание, что если литеральный regexp не содержит интерполяций, поведение o является стандартным.
Кодировки
По умолчанию, regexp только с символами 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>
Regexp может быть сопоставлен с целевой строкой, если:
-
У них одинаковая кодировка.
-
Кодировка regexp — это фиксированная кодировка, а строка содержит только символы ASCII.
MethodRegexp#fixed_encoding?возвращает, имеет ли regexp фиксированную кодировку.
Если выполняется попытка сопоставления между несовместимыми кодировками, возникает исключение 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 или целевая строка получены из ненадежного источника, вредоносные значения могут стать атакой на отказ в обслуживании; чтобы предотвратить такую атаку, рекомендуется установить таймаут.
Regexp имеет два значения таймаута:
-
По умолчанию таймаут класса, используемый для 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).
Сопоставление regexp может применять оптимизацию для предотвращения атак 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 книги):
-
Mastering Regular Expressions Джеффри Э.Ф. Фридла.
-
Regular Expressions Cookbook Яна Гойвертса и Стивена Левитана.
Изучать, тестировать (интерактивный онлайн-редактор):
Константы
- 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, возвращает новый regexp со строкой и параметрами:
r = Regexp.new('foo') # => /foo/
r.source # => "foo"
r.options # => 0
Необязательный аргумент options является одним из следующих:
-
Строка параметров:
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, которые игнорируются. -
Любое другое ненулевое значение, в таком случае regexp будет регистронезависимым.
Если указан необязательный ключевой аргумент timeout, его числовое значение переопределяет интервал таймаута для класса Regexp.timeout. Если nil передаётся как +timeout, используется интервал таймаута для класса Regexp.timeout.
С указанным аргументом regexp, возвращает новый 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:
Regexp.try_convert(/re/) # => /re/
В противном случае, если object отвечает на :to_regexp, вызывает object.to_regexp и возвращает результат.
Возвращает nil если object не отвечает на :to_regexp.
Regexp.try_convert('re') # => nil
Вызывает исключение, если object.to_regexp не возвращает 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);
} Возвращает новый regexp, являющийся объединением заданных шаблонов:
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, он используется как есть, включая его флаги:
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 # => /(?!)/
Если какой-либо шаблон regexp содержит захваты, поведение не определено.
Методы публичного экземпляра
Возвращает 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
Присваивает именованные capture локальным переменным с такими же именами, если и только если self:
-
Это regexp-литерал; см. Regexp-литералы.
-
Не содержит интерполяции; см. интерполяцию Regexp.
-
Находится слева от выражения.
Пример:
/(?<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 не является regexp-литералом:
r = /(?<foo>\w+)\s*=\s*(?<foo>\w+)/ r =~ ' x = y ' p foo # Undefined local variable p bar # Undefined local variable
Присваивание не происходит, если regexp не находится слева:
' x = y ' =~ /(?<foo>\w+)\s*=\s*(?<foo>\w+)/ p foo, foo # Undefined local variables
Интерполяция regexp, #{}, также отключает присваивание:
r = /(?<foo>\w+)/
/(?<foo>\w+)\s*=\s*#{r}/ =~ 'x = y'
p foo # Undefined local variable
# 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, возвращая хеш из 2 элементов, представляющий 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, представляющий кодировку obj.
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 для указания совпадения regexp или его отсутствия без обновления $~ и других связанных переменных. Если второй параметр присутствует, он указывает позицию в строке для начала поиска.
/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;
} Возвращает хеш, представляющий именованные capture self (см. Именованные capture):
-
Каждый ключ — имя именованного capture.
-
Каждое значение — массив целых индексов для этого именованного capture.
Примеры:
/(?<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;
} Возвращает массив имён capture (см. Именованные capture):
/(?<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 (см. 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 escape-последовательности сохраняются:
/\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.
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
Ruby Core © 1993–2022 Yukihiro Matsumoto
Licensed under the Ruby License.
Ruby Standard Library © contributors
Licensed under their own licenses.