Spec-Zone.ru › Ruby 3.2

класс Regexp

Родитель:
Объект

Регулярные выражения (regexp) — это шаблоны, описывающие содержимое строки. Они используются для проверки, содержит ли строка заданный шаблон, или для извлечения совпадающих частей. Они создаются с помощью литералов /pat/ и %r{pat} или конструктора Regexp.new.

Регулярное выражение обычно ограничено слешами (/). Например:

/hay/ =~ 'haystack'   #=> 0
/y/.match('haystack') #=> #<MatchData "y">

Если строка содержит шаблон, то говорят, что она совпадает. Литеральная строка совпадает сама с собой.

Здесь «стог сена» не содержит шаблон «игла», поэтому он не совпадает:

/needle/.match('haystack') #=> nil

Здесь «стог сена» содержит шаблон «стог», поэтому он совпадает:

/hay/.match('haystack')    #=> #<MatchData "hay">

В частности, /st/ требует, чтобы строка содержала букву s, за которой следует буква t, поэтому она совпадает со строкой стог сена.

Обратите внимание, что любое Regexp сопоставление вызовет RuntimeError, если таймаут установлен и превышен. Подробности см. в разделе “Таймаут”.

Интерполяция regexp

Регулярное выражение может содержать интерполированные строки; тривиально:

foo = 'bar'
/#{foo}/ # => /bar/

=~ и Regexp#match

Сопоставление с шаблоном можно выполнить с помощью оператора =~ или метода Regexp#match.

=~ Оператор

=~ — это базовый оператор сопоставления с шаблоном в Ruby. Когда один операнд — это регулярное выражение, а другой — это строка, то регулярное выражение используется в качестве шаблона для сопоставления со строкой. (Этот оператор эквивалентен определению Regexp и String, поэтому порядок String и Regexp не имеет значения. Другие классы могут иметь разные реализации оператора =~.) Если совпадение найдено, оператор возвращает индекс первого совпадения в строке, в противном случае возвращает nil.

/hay/ =~ 'haystack'   #=> 0
'haystack' =~ /hay/   #=> 0
/a/   =~ 'haystack'   #=> 1
/u/   =~ 'haystack'   #=> nil

Использование оператора =~ с String и Regexp устанавливает глобальную переменную $~ после успешного совпадения. $~ содержит объект MatchData. Regexp.last_match эквивалентно $~.

Regexp#match Method

Метод match возвращает объект MatchData:

/st/.match('haystack')   #=> #<MatchData "st">

Метасимволы и экранирование

Следующие являются метасимволами (, ), [, ], {, }, ., ?, +, *. Они имеют специальное значение при появлении в шаблоне. Для их буквального соответствия они должны быть экранированы обратной косой чертой. Для буквального соответствия обратной косой черты, экранируйте её: \\.

/1 \+ 2 = 3\?/.match('Does 1 + 2 = 3?') #=> #<MatchData "1 + 2 = 3?">
/a\\\\b/.match('a\\\\b')                    #=> #<MatchData "a\\b">

Шаблоны ведут себя как строки в двойных кавычках и могут содержать те же экранированные символы обратной косой чертой (значение \s отличается, однако, см. ниже).

/\s\u{6771 4eac 90fd}/.match("Go to 東京都")
    #=> #<MatchData " 東京都">

Произвольные выражения Ruby могут быть встроены в шаблоны с помощью конструкции #{...}.

place = "東京都"
/#{place}/.match("Go to 東京都")
    #=> #<MatchData "東京都">

Классы символов

Класс символов ограничен квадратными скобками ([, ]) и перечисляет символы, которые могут появиться в этом месте соответствия. /[ab]/ означает а или б, в отличие от /ab/ , которое означает а, за которым следует б.

/W[aeiou]rd/.match("Word") #=> #<MatchData "Word">

Внутри класса символов дефис (-) — это метасимвол, обозначающий интервал символов. [abcd] эквивалентно [a-d]. Интервал может следовать за другим интервалом, поэтому [abcdwxyz] эквивалентно [a-dw-z]. Порядок, в котором появляются интервалы или отдельные символы внутри класса символов, не имеет значения.

/[0-9a-f]/.match('9f') #=> #<MatchData "9">
/[9f]/.match('9f')     #=> #<MatchData "9">

Если первый символ класса символов — это символ вставки (^), класс инвертируется: он совпадает с любым символом кроме указанных.

/[^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]/

Следующие метасимволы также ведут себя как классы символов:

  • /./ — Любой символ, кроме новой строки.

  • /./m — Любой символ (модификатор m включает многострочный режим)

  • /\w/ — Символ слова ([a-zA-Z0-9_])

  • /\W/ — Несимвол слова ([^a-zA-Z0-9_]). Обратитесь к Ошибке #4044, если используете /\W/ с модификатором /i.

  • /\d/ — Символ цифры ([0-9])

  • /\D/ — Несимвол цифры ([^0-9])

  • /\h/ — Символ шестнадцатеричной цифры ([0-9a-fA-F])

  • /\H/ — Несимвол шестнадцатеричной цифры ([^0-9a-fA-F])

  • /\s/ — Символ пробела: /[ \t\r\n\f\v]/

  • /\S/ — Несимвол пробела: /[^ \t\r\n\f\v]/

  • /\R/ — Символ перевода строки: \n, \v, \f, \r \u0085 (следующая строка), \u2028 (разделитель строк), \u2029 (разделитель абзацев) или \r\n.

POSIX скобочные выражения также похожи на классы символов. Они предлагают портативную альтернативу вышеупомянутому, с дополнительным преимуществом, что они охватывают не-ASCII символы. Например, /\d/ соответствует только ASCII десятичным цифрам (0-9); в то время как /[[:digit:]]/ соответствует любому символу в категории Unicode Nd.

  • /[[:alnum:]]/ - Алфавитно-цифровой символ

  • /[[:alpha:]]/ - Алфавитный символ

  • /[[:blank:]]/ - Пробел или табуляция

  • /[[:cntrl:]]/ - Символ управления

  • /[[:digit:]]/ - Цифра

  • /[[:graph:]]/ - Непустой символ (исключает пробелы, управляющие символы и подобные)

  • /[[:lower:]]/ - Символ строчной буквы алфавита

  • /[[:print:]]/ - Как [:graph:], но включает символ пробела

  • /[[:punct:]]/ - Символ пунктуации

  • /[[:space:]]/ - Символ пробела ([:blank:], перевод строки, возврат каретки и т. д.)

  • /[[:upper:]]/ - Символ заглавной буквы алфавита

  • /[[:xdigit:]]/ - Цифра, допустимая в шестнадцатеричном числе (т. е., 0-9a-fA-F)

Ruby также поддерживает следующие не-POSIX классы символов:

  • /[[:word:]]/ - Символ в одной из следующих категорий Unicode Letter, Mark, Number, Connector_Punctuation

  • /[[:ascii:]]/ - Символ ASCII набора символов

    # U+06F2 is "EXTENDED ARABIC-INDIC DIGIT TWO"
    /[[:digit:]]/.match("\u06F2")    #=> #<MatchData "\u{06F2}">
    /[[:upper:]][[:lower:]]/.match("Hello") #=> #<MatchData "He">
    /[[:xdigit:]][[:xdigit:]]/.match("A6")  #=> #<MatchData "A6">
    

Повторение

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

  • * - Ноль или более раз

  • + - Один или более раз

  • ? - Ноль или один раз (необязательно)

  • {n} - Ровно n раз

  • {n,} - n или более раз

  • {,m} - m или меньше раз

  • {n,m} - По крайней мере n и не более m раз

По крайней мере одна заглавная буква (‘H’), по крайней мере одна строчная буква (‘e’), две буквы ‘l’, затем одна буква ‘o’:

"Hello".match(/[[:upper:]]+[[:lower:]]+l{2}o/) #=> #<MatchData "Hello">

Жадное соответствие

По умолчанию повторение жадное: соответствует как можно большему количеству вхождений, при этом все еще позволяя общему совпадению произойти. В отличие от этого, ленивое сопоставление делает минимальное количество совпадений, необходимое для общего успеха. Большинство жадных метасимволов можно сделать ленивыми, добавив за ними ?. Для шаблона {n}, поскольку он указывает на точное количество символов для сопоставления, а не на переменное число символов, метасимвол ? вместо этого делает повторяемый шаблон необязательным.

Оба шаблона ниже совпадают со строкой. Первый использует жадный квантификатор, поэтому ‘.+’ совпадает с ‘<a><b>’; второй использует ленивый квантификатор, поэтому ‘.+?’ совпадает с ‘<a>’:

/<.+>/.match("<a><b>")  #=> #<MatchData "<a><b>">
/<.+?>/.match("<a><b>") #=> #<MatchData "<a>">

Позитивное соответствие

Квантификатор, за которым следует +, соответствует положительно: после соответствия он не возвращается. Они ведут себя как жадные квантификаторы, но, выполнив соответствие, отказываются «отказываться» от своего соответствия даже если это ставит под угрозу общее соответствие.

/<.*><.+>/.match("<a><b>") #=> #<MatchData "<a><b>">
/<.*+><.+>/.match("<a><b>") #=> nil
/<.*><.++>/.match("<a><b>") #=> nil

Захват

Скобки могут использоваться для захвата. Текст, заключенный в n-ой группе скобок, может быть ссылаться на него позже с помощью n. Внутри шаблона используйте ссылку на обратное обращение \n (например, \1); вне шаблона используйте MatchData[n] (например, MatchData[1]).

В этом примере 'at' захватывается первой группой скобок, а затем ссылается на неё позднее с помощью \1:

/[csh](..) [csh]\1 in/.match("The cat sat in the hat")
    #=> #<MatchData "cat sat in" 1:"at">

Regexp#match возвращает объект MatchData, который делает доступным захваченный текст с помощью метода []:

/[csh](..) [csh]\1 in/.match("The cat sat in the hat")[1] #=> 'at'

Хотя Ruby поддерживает произвольное количество пронумерованных захваченных групп, только группы 1-9 поддерживаются с помощью синтаксиса обратной ссылки \n.

Ruby также поддерживает \0 в качестве специальной обратной ссылки, которая ссылается на всю сопоставленную строку. Это также доступно в MatchData[0]. Обратите внимание, что обратная ссылка \0 не может быть использована внутри регулярного выражения, так как обратные ссылки могут быть использованы только после окончания группы захвата, а обратная ссылка \0 использует неявную группу захвата всего совпадения. Однако вы можете использовать эту обратную ссылку при выполнении подстановки:

"The cat sat in the hat".gsub(/[csh]at/, '\0s')
  # => "The cats sats in the hats"

Именованные захваты

К группам захвата можно обращаться по имени, когда они определены с помощью конструкций (?<имя>) или (?'имя').

/\$(?<dollars>\d+)\.(?<cents>\d+)/.match("$3.67")
    #=> #<MatchData "$3.67" dollars:"3" cents:"67">
/\$(?<dollars>\d+)\.(?<cents>\d+)/.match("$3.67")[:dollars] #=> "3"

Именованные группы могут быть обработаны с помощью обратной ссылки \k<имя>, где имя — имя группы.

/(?<vowel>[aeiou]).\k<vowel>.\k<vowel>/.match('ototomy')
    #=> #<MatchData "ototo" vowel:"o">

Примечание: регулярное выражение не может одновременно использовать именованные и пронумерованные обратные ссылки. Кроме того, если именованный захват используется в регулярном выражении, то скобки, используемые для группировки, которые в противном случае привели бы к безымянному захвату, обрабатываются как незахватывающие.

/(\w)(\w)/.match("ab").captures # => ["a", "b"]
/(\w)(\w)/.match("ab").named_captures # => {}

/(?<c>\w)(\w)/.match("ab").captures # => ["a"]
/(?<c>\w)(\w)/.match("ab").named_captures # => {"c"=>"a"}

При использовании именованных групп захвата с литеральным регулярным выражением в левой части выражения и оператором =~, захваченный текст также присваивается локальным переменным с соответствующими именами.

/\$(?<dollars>\d+)\.(?<cents>\d+)/ =~ "$3.67" #=> 0
dollars #=> "3"

Группирование

Скобки также группируют термины, которые они заключают, что позволяет им быть квантифицированными как единое атомарное целое.

Нижеприведенный шаблон соответствует гласной, за которой следуют 2 символа слова:

/[aeiou]\w{2}/.match("Caenorhabditis elegans") #=> #<MatchData "aen">

В то время как следующий шаблон соответствует гласной, за которой следует символ слова дважды, т.е. [aeiou]\w[aeiou]\w: ‘enor’.

/([aeiou]\w){2}/.match("Caenorhabditis elegans")
    #=> #<MatchData "enor" 1:"or">

Конструкция (?:…) обеспечивает группирование без захвата. То есть, она объединяет содержащиеся в ней термины в атомарное целое без создания обратной ссылки. Это улучшает производительность в ущерб удобочитаемости.

Первая группа скобок захватывает «n», а вторая — «ti». Ко второй группе позже обращаются с помощью обратной ссылки \2:

/I(n)ves(ti)ga\2ons/.match("Investigations")
    #=> #<MatchData "Investigations" 1:"n" 2:"ti">

Первая группа скобок теперь сделана незахватывающей с помощью ‘?:’, поэтому она всё ещё соответствует «n», но не создаёт обратной ссылки. Таким образом, обратная ссылка \1 теперь ссылается на «ti».

/I(?:n)ves(ti)ga\1ons/.match("Investigations")
    #=> #<MatchData "Investigations" 1:"ti">

Атомарное группирование

Группирование может быть сделано атомарным с помощью (?>pat). Это заставляет подвыражение pat соответствовать независимо от остальной части выражения, так что то, что оно соответствует, становится фиксированным для остальной части совпадения, если только всё подвыражение не должно быть оставлено и впоследствии пересмотрено. Таким образом, pat рассматривается как неделимое целое. Атомарное группирование обычно используется для оптимизации шаблонов, чтобы предотвратить ненужное возвращение к регулярному выражению.

" в шаблоне ниже соответствует первому символу строки, а затем .* соответствует Quote“. Это приводит к тому, что общее совпадение терпит неудачу, поэтому текст, соответствующий .* отступает на одну позицию, что оставляет последний символ строки, доступный для совпадения "

/".*"/.match('"Quote"')     #=> #<MatchData "\"Quote\"">

Если .* сгруппировано атомарно, она отказывается от возврата к Quote“, даже если это означает, что общее совпадение терпит неудачу

/"(?>.*)"/.match('"Quote"') #=> nil

Вызовы подвыражений

Синтаксис \g<имя> соответствует предыдущему подвыражению с именем имя, которое может быть именем или номером группы, снова. Это отличается от обратных ссылок тем, что оно повторно выполняет группу, а не просто пытается повторно сопоставить тот же текст.

Этот шаблон соответствует символу ( и присваивает его группе paren, пытается снова вызвать подвыражение paren, но терпит неудачу, а затем соответствует литеральному символу ):

/\A(?<paren>\(\g<paren>*\))*\z/ =~ '()'

/\A(?<paren>\(\g<paren>*\))*\z/ =~ '(())' #=> 0
# ^1
#      ^2
#           ^3
#                 ^4
#      ^5
#           ^6
#                      ^7
#                       ^8
#                       ^9
#                           ^10
  1. Совпадает в начале строки, т.е. перед первым символом.

  2. Входит в именованную группу захвата под названием paren

  3. Совпадает с литеральным символом (, первым символом в строке

  4. Вызывает группу paren снова, т.е. рекурсивно возвращается к второму шагу

  5. Повторно входит в группу paren

  6. Совпадает с литеральным символом (, вторым символом в строке

  7. Попытка вызвать paren в третий раз, но терпит неудачу, потому что это помешает общему успешному совпадению

  8. Совпадает с литеральным символом ), третьим символом в строке. Помечает конец второго рекурсивного вызова

  9. Совпадает с литеральным символом ), четвёртым символом в строке

  10. Совпадает с концом строки

Альтернация

Метасимвол вертикальной черты (|) объединяет несколько выражений в одно, которое соответствует любому из них. Каждое выражение является альтернативой.

/\w(and|or)\w/.match("Feliformia") #=> #<MatchData "form" 1:"or">
/\w(and|or)\w/.match("furandi")    #=> #<MatchData "randi" 1:"and">
/\w(and|or)\w/.match("dissemblance") #=> nil

Свойства символов

Конструкция \p{} соответствует символам с заданным свойством, очень похоже на POSIX квадратные скобки.

  • /\p{Alnum}/ - Буквальный и числовой символ

  • /\p{Alpha}/ - Буквальный символ

  • /\p{Blank}/ - Пробел или табуляция

  • /\p{Cntrl}/ - Символ управления

  • /\p{Digit}/ - Цифра

  • /\p{Emoji}/ - Юникод эмодзи

  • /\p{Graph}/ - Символ, не являющийся пробелом (исключает пробелы, управляющие символы и подобные)

  • /\p{Lower}/ - Строчный буквенный символ

  • /\p{Print}/ - Как \p{Graph}, но включает и пробел

  • /\p{Punct}/ - Символ пунктуации

  • /\p{Space}/ - Символ пробела ([:blank:], перевод строки, возврат каретки и т.д.)

  • /\p{Upper}/ - Прописной буквенный символ

  • /\p{XDigit}/ - Цифра, разрешенная в шестнадцатеричном числе (т.е., 0-9a-fA-F)

  • /\p{Word}/ - Символ, являющийся частью одной из следующих универсальных категорий Юникода: Letter, Mark, Number, Connector_Punctuation

  • /\p{ASCII}/ - Символ из набора символов ASCII

  • /\p{Any}/ - Любой символ Юникода (включая неназначенные символы)

  • /\p{Assigned}/ - Назначенный символ

Значение Универсальной категории символа Юникода также может быть сопоставлено с помощью \p{Ab}, где Ab — сокращение категории, как описано ниже:

  • /\p{L}/ - ‘Letter’

  • /\p{Ll}/ - ‘Letter: Lowercase’

  • /\p{Lm}/ - ‘Letter: Mark’

  • /\p{Lo}/ - ‘Letter: Other’

  • /\p{Lt}/ - ‘Letter: Titlecase’

  • /\p{Lu}/ - ‘Letter: Uppercase

  • /\p{Lo}/ - ‘Letter: Other’

  • /\p{M}/ - ‘Mark’

  • /\p{Mn}/ - ‘Mark: Nonspacing’

  • /\p{Mc}/ - ‘Mark: Spacing Combining’

  • /\p{Me}/ - ‘Mark: Enclosing’

  • /\p{N}/ - ‘Number’

  • /\p{Nd}/ - ‘Number: Decimal Digit’

  • /\p{Nl}/ - ‘Number: Letter’

  • /\p{No}/ - ‘Number: Other’

  • /\p{P}/ - ‘Punctuation’

  • /\p{Pc}/ - ‘Punctuation: Connector’

  • /\p{Pd}/ - ‘Punctuation: Dash’

  • /\p{Ps}/ - ‘Punctuation: Open’

  • /\p{Pe}/ - ‘Punctuation: Close’

  • /\p{Pi}/ - ‘Punctuation: Initial Quote’

  • /\p{Pf}/ - ‘Punctuation: Final Quote’

  • /\p{Po}/ - ‘Punctuation: Other’

  • /\p{S}/ - ‘Symbol’

  • /\p{Sm}/ - ‘Symbol: Math’

  • /\p{Sc}/ - ‘Symbol: Currency’

  • /\p{Sc}/ - ‘Symbol: Currency’

  • /\p{Sk}/ - ‘Symbol: Modifier’

  • /\p{So}/ - ‘Symbol: Other’

  • /\p{Z}/ - ‘Separator’

  • /\p{Zs}/ - ‘Separator: Space’

  • /\p{Zl}/ - ‘Separator: Line’

  • /\p{Zp}/ - ‘Separator: Paragraph’

  • /\p{C}/ - ‘Other’

  • /\p{Cc}/ - ‘Other: Control’

  • /\p{Cf}/ - ‘Other: Format’

  • /\p{Cn}/ - ‘Other: Not Assigned’

  • /\p{Co}/ - ‘Other: Private Use’

  • /\p{Cs}/ - ‘Other: Surrogate’

END_OF_DOCUMENT_MARKER

Наконец, \p{} соответствует Unicode-скрипту символа. Поддерживаются следующие скрипты: арабский, армянский, балийский, бенгальский, бопомофо, брайлевский, бугинский, бухидский, канадский_аборигенный, карийский, чамский, чероки, общий, коптский, клинопись, кипрский, кириллический, дезерет, деванагари, эфиопский, грузинский, глаголический, готический, греческий, гуджаратский, гурмухи, хань, хангыль, хануноо, еврейский, хирагана, унаследованный, каннада, катакана, каях_ли, харошты, кхмерский, лаосский, латинский, лепча, лимбу, линейный_b, ликийский, лидийский, малаялам, монгольский, бирманский, новый_тай_лю, нко, огам, ол_чики, древнеиталийский, древнеперсидский, ория, османский, пагс_па, финикийский, режанг, рунический, саураштра, шавийский, сингальский, сунданесский, силоти_нагри, сирийский, тагальский, тагбанва, тай_ле, тамильский, телугу, тана, тайский, тибетский, тифинаг, угаритский, вай и и.

Unicode-код U+06E9 называется «АРАБСКОЕ МЕСТО САЙДАХ» и относится к арабскому скрипту:

/\p{Arabic}/.match("\u06E9") #=> #<MatchData "\u06E9">

Все свойства символов можно инвертировать, добавив к их имени символ «карет» (^).

Буква «A» не входит в категорию Unicode Ll (Буква; строчная буква), поэтому этот соответствие успешно:

/\p{^Ll}/.match("A") #=> #<MatchData "A">

Якоря

Якоря — это метасимволы, которые соответствуют позициям нулевой ширины между символами, привязывая соответствие к определённой позиции.

  • ^ — Соответствует началу строки

  • $ — Соответствует концу строки

  • \A — Соответствует началу строки.

  • \Z — Соответствует концу строки. Если строка заканчивается символом новой строки, соответствует позиции перед символом новой строки

  • \z — Соответствует концу строки

  • \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
    
  • \b — Соответствует границам слов вне скобок; пробел назад (0x08) внутри скобок

  • \B — Соответствует границам неслов

  • (?=pat) — Положительное утверждение оглядывания вперёд: гарантирует, что следующие символы соответствуют pat, но не включает эти символы в сопоставленный текст

  • (?!pat) — Отрицательное утверждение оглядывания вперёд: гарантирует, что следующие символы не соответствуют pat, но не включает эти символы в сопоставленный текст

  • (?<=pat) — Положительное утверждение оглядывания назад: гарантирует, что предыдущие символы соответствуют pat, но не включает эти символы в сопоставленный текст

  • (?<!pat) — Отрицательное утверждение оглядывания назад: гарантирует, что предыдущие символы не соответствуют pat, но не включает эти символы в сопоставленный текст

  • \K — Сброс соответствия: сопоставленный контент, предшествующий \K в регулярном выражении, исключается из результата. Например, следующие два регулярных выражения почти эквивалентны:

    /ab\Kc/ =~ "abc"     #=> 0
    /(?<=ab)c/ =~ "abc"  #=> 2
    

    Они соответствуют одной и той же строке, и $& равно "c", в то время как позиция соответствия отличается.

    Также как и следующие два регулярных выражения:

    /(a)\K(b)\Kc/
    /(?<=(?<=(a))(b))c/
    

Если шаблон не привязан, он может начинаться в любой точке строки:

/real/.match("surrealist") #=> #<MatchData "real">

Привязка шаблона к началу строки заставляет соответствие начинаться там. ‘real’ не встречается в начале строки, поэтому соответствие не удается:

/\Areal/.match("surrealist") #=> nil

Соответствие ниже не удается, потому что, хотя «Demand» содержит «and», шаблон не появляется на границе слова.

/\band/.match("Demand")

В то время как в следующем примере «and» привязано к границе неслова, поэтому вместо соответствия первому «and» оно соответствует с четвёртого символа «demand»:

/\Band.+/.match("Supply and demand curve") #=> #<MatchData "and curve">

Шаблон ниже использует положительное оглядывание вперёд и положительное оглядывание назад для сопоставления текста, появляющегося в тегах, без включения тегов в соответствие:

/(?<=<b>)\w+(?=<\/b>)/.match("Fortune favours the <b>bold</b>")
    #=> #<MatchData "bold">

Параметры

Конечный разделитель регулярного выражения может быть последан одним или более одиночными параметрами, которые контролируют, как может выполняться соответствие шаблону.

  • /pat/i — Игнорировать регистр

  • /pat/m — Рассматривать новую строку как символ, сопоставленный с .

  • /pat/x — Игнорировать пробелы и комментарии в шаблоне

  • /pat/o — Выполнить интерполяцию #{} только один раз

i, m, и x также могут быть применены на уровне подвыражения с помощью конструкции (?on-off), которая включает параметры on и отключает параметры off для выражения, заключенного в скобки:

/a(?i:b)c/.match('aBc')   #=> #<MatchData "aBc">
/a(?-i:b)c/i.match('ABC') #=> nil

Кроме того, эти параметры также могут быть переключены для остальной части шаблона:

/a(?i)bc/.match('abC') #=> #<MatchData "abC">

Параметры также могут быть использованы с Regexp.new:

Regexp.new("abc", Regexp::IGNORECASE)                     #=> /abc/i
Regexp.new("abc", Regexp::MULTILINE)                      #=> /abc/m
Regexp.new("abc # Comment", Regexp::EXTENDED)             #=> /abc # Comment/x
Regexp.new("abc", Regexp::IGNORECASE | Regexp::MULTILINE) #=> /abc/mi

Regexp.new("abc", "i")           #=> /abc/i
Regexp.new("abc", "m")           #=> /abc/m
Regexp.new("abc # Comment", "x") #=> /abc # Comment/x
Regexp.new("abc", "im")          #=> /abc/mi

Режим свободного форматирования и комментарии

Как упоминалось выше, параметр x включает режим свободного форматирования. Литеральные пробелы внутри шаблона игнорируются, а символ octothorpe (#) вводит комментарий до конца строки. Это позволяет организовать компоненты шаблона потенциально более читаемым способом.

Выдуманный шаблон для сопоставления числа с необязательной десятичной частью:

float_pat = /\A
    [[:digit:]]+ # 1 or more digits before the decimal point
    (\.          # Decimal point
        [[:digit:]]+ # 1 or more digits after the decimal point
    )? # The decimal point and following digits are optional
\Z/x
float_pat.match('3.14') #=> #<MatchData "3.14" 1:".14">

Существует несколько стратегий для сопоставления пробелов:

  • Используйте шаблон, например, \s или \p{Space}.

  • Используйте экранированные пробелы, например, \ , т.е. пробел, предшествуемый обратным слэшем.

  • Используйте класс символов, например, [ ].

Комментарии могут быть включены в шаблон, не являющийся x с помощью конструкции (?#comment), где comment — произвольный текст, игнорируемый движком регулярных выражений.

Комментарии в литералах регулярных выражений не могут содержать неэкранированные символы-разделители.

Encoding

Предполагается, что регулярные выражения используют кодировку источника. Это можно переопределить с помощью одного из следующих модификаторов.

  • /pat/u — UTF-8

  • /pat/e — EUC-JP

  • /pat/s — Windows-31J

  • /pat/n — ASCII-8BIT

Регулярное выражение может быть сопоставлено со строкой, когда они либо разделяют кодировку, либо кодировка регулярного выражения равна US-ASCII, а кодировка строки совместима с ASCII.

Если попытка соответствия между несовместимыми кодировками, генерируется исключение Encoding::CompatibilityError.

Предикат Regexp#fixed_encoding? указывает, имеет ли регулярное выражение фиксированную кодировку, то есть кодировку, несовместимую с ASCII. Кодировка регулярного выражения может быть явно установлена, задав Regexp::FIXEDENCODING в качестве второго аргумента Regexp.new:

r = Regexp.new("a".force_encoding("iso-8859-1"),Regexp::FIXEDENCODING)
r =~ "a\u3042"
   # raises Encoding::CompatibilityError: incompatible encoding regexp match
   #         (ISO-8859-1 regexp with UTF-8 string)

Глобальные переменные Regexp

Сопоставление шаблонов устанавливает некоторые глобальные переменные:

  • $~ эквивалентно Regexp.last_match;

  • $& содержит весь сопоставленный текст;

  • $` содержит строку перед соответствием;

  • $' содержит строку после соответствия;

  • $1, $2 и т. д. содержат текст, соответствующий первой, второй и т. д. группе захвата;

  • $+ содержит последнюю группу захвата.

Пример:

m = /s(\w{2}).*(c)/.match('haystack') #=> #<MatchData "stac" 1:"ta" 2:"c">
$~                                    #=> #<MatchData "stac" 1:"ta" 2:"c">
Regexp.last_match                     #=> #<MatchData "stac" 1:"ta" 2:"c">

$&      #=> "stac"
        # same as m[0]
$`      #=> "hay"
        # same as m.pre_match
$'      #=> "k"
        # same as m.post_match
$1      #=> "ta"
        # same as m[1]
$2      #=> "c"
        # same as m[2]
$3      #=> nil
        # no third group in pattern
$+      #=> "c"
        # same as m[-1]

Эти глобальные переменные являются локальными переменными потока и локальными переменными метода.

Производительность

Определённые патологические комбинации конструкций могут привести к катастрофически низкой производительности.

Рассмотрим строку из 25 a, d, 4 a и c.

s = 'a' * 25 + 'd' + 'a' * 4 + 'c'
#=> "aaaaaaaaaaaaaaaaaaaaaaaaadaaaac"

Следующие шаблоны сопоставляются мгновенно, как вы и ожидали:

/(b|a)/ =~ s #=> 0
/(b|a+)/ =~ s #=> 0
/(b|a+)*/ =~ s #=> 0

Однако, следующий шаблон сопоставляется значительно дольше:

/(b|a+)*c/ =~ s #=> 26

Это происходит потому, что атом в регулярном выражении квантифицируется как непосредственным +, так и охватывающим *, без чего-либо, что отличает, какой из них управляет любым конкретным символом. Возникающий недетерминизм приводит к производительности, зависящей от степени превышения линейной зависимости. (См. Mastering Regular Expressions (3-е изд.), стр. 222, авторы Jeffery Friedl, для глубокого анализа). Этот конкретный случай можно исправить, используя атомную группировку, которая предотвращает ненужное возвращение назад:

(start = Time.now) && /(b|a+)*c/ =~ s && (Time.now - start)
   #=> 24.702736882
(start = Time.now) && /(?>b|a+)*c/ =~ s && (Time.now - start)
   #=> 0.000166571

Похожий случай иллюстрируется следующим примером, который занимает примерно 60 секунд для выполнения у меня:

Сопоставить строку из 29 a со шаблоном из 29 необязательных a, после которых следуют 29 обязательных a:

Regexp.new('a?' * 29 + 'a' * 29) =~ 'a' * 29

29 необязательных a сопоставляются со строкой, но это препятствует сопоставлению последующих 29 обязательных a. Ruby должен многократно возвращаться назад, чтобы удовлетворить как можно больше совпадений с необязательными параметрами, сохраняя при этом совпадение с обязательными 29. Для нас очевидно, что ни одно из необязательных совпадений не может быть успешным, но этот факт к сожалению ускользает от Ruby.

Лучший способ повысить производительность — значительно уменьшить количество необходимых возвращений назад. В данном случае вместо индивидуального сопоставления 29 необязательных a, можно сопоставить диапазон необязательных a сразу с помощью a{0,29}:

Regexp.new('a{0,29}' + 'a' * 29) =~ 'a' * 29

Timeout

Существует два API для установки таймаута. Один из них — Regexp.timeout=, который представляет собой глобальную для процесса настройку таймаута для Regexp сопоставления.

Regexp.timeout = 3
s = 'a' * 25 + 'd' + 'a' * 4 + 'c'
/(b|a+)*c/ =~ s  #=> This raises an exception in three seconds

Другой — ключевое слово timeout в Regexp.new.

re = Regexp.new("(b|a+)*c", timeout: 3)
s = 'a' * 25 + 'd' + 'a' * 4 + 'c'
/(b|a+)*c/ =~ s  #=> This raises an exception in three seconds

При использовании регулярных выражений для обработки ненадежных данных следует использовать функцию таймаута, чтобы избежать чрезмерной обратной трассировки. В противном случае злоумышленник может предоставить входные данные для Regexp, вызвав атаку типа «отказ в обслуживании». Обратите внимание, что таймаут не установлен по умолчанию, поскольку подходящее ограничение сильно зависит от требований и контекста приложения.

Константы

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

Методы публичного класса

compile(*args)

Псевдоним для Regexp.new

escape(string) → new_string Показать исходный код
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 "\\\\\\*\\?\\{\\}\\.">

Regexp.quote является псевдонимом для Regexp.escape.

json_create(object) Показать исходный код
# File ext/json/lib/json/add/regexp.rb, line 11
def self.json_create(object)
  new(object['s'], object['o'])
end

Десериализует строку JSON, создавая новый объект Regexp с исходным кодом s (Regexp или String) и опциями o, сериализованными to_json

last_match → matchdata or nil Показать исходный код
last_match(n) → string or nil
last_match(name) → string or nil
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();
}

Без аргумента возвращает значение $!, которое является результатом последнего сопоставления с шаблоном (см. Переменные среды Регулярных Выражений):

/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.
linear_time?(re) Показать исходный код
linear_time?(string, options = 0)
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, а не аргумента регулярного выражения. Идентичные regexp могут или не могут выполняться за линейное время в зависимости от вашей версии Ruby. Гарантий ни вперёд, ни назад о возвращаемом значении этого метода нет. Наш текущий алгоритм — (*1), но это может измениться в будущем. Альтернативные реализации также могут вести себя по-разному. Они могут всегда возвращать false для всего.

(*1): doi.org/10.1109/SP40001.2021.00032

new(string, options = 0, timeout: nil) → regexp Показать исходный код
new(regexp, timeout: nil) → regexp
static VALUE
rb_reg_initialize_m(int argc, VALUE *argv, VALUE self)
{
    struct reg_init_args args;

    reg_extract_args(argc, argv, &args);
    reg_init_args(self, args.str, args.enc, args.flags);

    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, которые игнорируются.

Если задан необязательный ключевой аргумент 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

Regexp.compile является псевдонимом для Regexp.new.

escape(string) → new_string Показать исходный код
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 "\\\\\\*\\?\\{\\}\\.">

Regexp.quote является псевдонимом для Regexp.escape.

timeout → float or nil Показать исходный код
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 означает отсутствие конфигурации таймаута по умолчанию.

timeout = float or 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)
try_convert(object) → regexp or nil Показать исходный код
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.

union(*patterns) → regexp Показать исходный код
union(array_of_patterns) → 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 содержит захваты, поведение не определено.

END_OF_DOCUMENT_MARKER

Методы экземпляров публичного интерфейса

regexp == object → true или false

Возвращает 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

Regexp#eql? — псевдоним для Regexp#==.

Псевдоним для: eql?
regexp === string → true или 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"
regexp =~ string → целое число или nil Показать исходный код
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
as_json(*) Показать исходный код
# File ext/json/lib/json/add/regexp.rb, line 17
def as_json(*)
  {
    JSON.create_id => self.class.name,
    'o'            => options,
    's'            => source,
  }
end

Возвращает хеш, который будет преобразован в объект JSON и представлять этот объект.

casefold?→ true или false Показать исходный код
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
encoding → encoding Показать исходный код
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, представляющий кодировку объекта.

eql? == object -> true или false Показать исходный код
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);
}

Возвращает 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

Regexp#eql? — псевдоним для Regexp#==.

Также псевдоним: ==
fixed_encoding? → true или false Показать исходный код
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
hash → целое число Показать исходный код
VALUE
rb_reg_hash(VALUE re)
{
    st_index_t hashval = reg_hash(re);
    return ST2FIX(hashval);
}

Возвращает целочисленное значение хеша для self.

Связанно с: Object#hash.

inspect → строка Показать исходный код
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(RREGEXP_SRC_PTR(re), RREGEXP_SRC_LEN(re), re);
}

Возвращает красиво отформатированное строковое представление self:

/ab+c/ix.inspect # => "/ab+c/ix"

Связанно с: Regexp#to_s.

match(string, offset = 0) → matchdata или nil Показать исходный код
match(string, offset = 0) {|matchdata| ... } → объект
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"
match?(string) → true или false Показать исходный код
match?(string, offset = 0) → true или false
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
named_captures → хеш Показать исходный код
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             # => {}
names → массив_имён Показать исходный код
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                      # => []
options → целое число Показать исходный код
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/
source → строка Показать исходный код
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  # => "/"
timeout → число с плавающей точкой или nil Показать исходный код
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)
to_json(*args) Показать исходный код
# File ext/json/lib/json/add/regexp.rb, line 27
def to_json(*args)
  as_json.to_json(*args)
end

Сохраняет имя класса (Regexp) с параметрами o и исходным кодом s (Regexp или String) как строку в формате JSON.

to_s → строка Показать исходный код
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 или как интерполированный текст для литерала Regexp:

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.

~ rxp → целое число или nil Показать исходный код
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.

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API