Spec-Zone.ru › Ruby 2.5

класс Regexp

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

A Regexp содержит регулярное выражение, используемое для сопоставления шаблона со строками. Регулярные выражения создаются с помощью /.../ и %r{...} литералов и конструктора Regexp::new.

Регулярные выражения (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, поэтому она также совпадает с haystack.

=~ и #match

Сопоставление шаблонов можно выполнить, используя оператор =~ или метод #match.

=~ оператор

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

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

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

#match метод

Метод 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\u{6771 4eac 90fd}/.match("Go to 東京都")
    #=> #<MatchData " 東京都">

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

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

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

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

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

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">

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

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

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

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

Захват

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

«at» захватывается первой группой скобок, затем на него ссылаются позже с помощью \1:

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

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

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

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

/\$(?<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">

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

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

/\$(?<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<name> соответствует предыдущему подвыражению с именем name, которое может быть именем группы или номером, ещё раз. Это отличается от обратных ссылок тем, что оно повторно выполняет группу, а не просто пытается повторно сопоставить тот же текст.

Этот шаблон соответствует символу ( и присваивает его группе 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{Graph}/ - Символ, не являющийся пробелом (исключая пробелы, управляющие символы и аналогичные)

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

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

  • /\p{Punct}/ - Знак препинания

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

  • /\p{Upper}/ - Символ заглавной буквы

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

  • /\p{Word}/ - Член одной из следующих категорий Unicode: Letter, Mark, Number, Connector_Punctuation

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

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

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

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

И наконец, \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, но не включает эти символы в сопоставленный текст

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

/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">

Параметры

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

  • /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/.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

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

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

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

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 — произвольный текст, игнорируемый движком regexp.

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

Кодировка

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

  • /pat/u - UTF-8

  • /pat/e - EUC-JP

  • /pat/s - Windows-31J

  • /pat/n - ASCII-8BIT

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

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

Предикат Regexp#fixed_encoding? указывает, имеет ли regexp фиксированную кодировку, то есть несовместимую с ASCII. Кодировку regexp можно явно зафиксировать, передав 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)

Специальные глобальные переменные

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

  • $~ эквивалентно ::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

Это происходит потому, что атом в regexp квантифицируется как немедленным +, так и окружающим *, без чего-либо, что могло бы различать, какой из них управляет тем или иным символом. Возникающая неоднозначность приводит к сверхлинейной производительности. (Обратитесь к книге «Мастерство регулярных выражений» (3-е изд.), стр. 222, Джеффри Фридл, для углубленного анализа). Этот конкретный случай можно исправить, используя атомарные группировки, которые предотвращают ненужное возвращение назад:

(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

Константы

EXTENDED

см. #options и ::new

FIXEDENCODING

см. #options и ::new

IGNORECASE

см. #options и ::new

MULTILINE

см. #options и ::new

NOENCODING

см. #options и ::new

Публичные методы класса

compile(*args)

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

escape(str) → string Показать исходный код
static VALUE
rb_reg_s_quote(VALUE c, VALUE str)
{
    return rb_reg_quote(reg_operand(str, TRUE));
}

Экранирует любые символы, которые имели бы специальное значение в регулярном выражении. Возвращает новую экранированную строку или self, если нет символов для экранирования. Для любой строки Regexp.new(Regexp.escape(str))=~str будет истинным.

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 Показать исходный код
last_match(n) → str
static VALUE
rb_reg_s_last_match(int argc, VALUE *argv)
{
    VALUE nth;

    if (argc > 0 && rb_scan_args(argc, argv, "01", &nth) == 1) {
        VALUE match = rb_backref_get();
        int n;
        if (NIL_P(match)) return Qnil;
        n = match_backref_number(match, nth);
        return rb_reg_nth_match(n, match);
    }
    return match_getter();
}

Первый вариант возвращает объект MatchData, сгенерированный последним успешным сопоставлением шаблона. Эквивалентно чтению специальной глобальной переменной $~ (см. Специальные глобальные переменные в Regexp для подробностей).

Второй вариант возвращает n-е поле в этом объекте MatchData. n может быть строкой или символом для ссылки на именованный захват.

Обратите внимание, что ::last_match локален для потока и области метода, который выполнил сопоставление шаблона.

/c(.)t/ =~ 'cat'        #=> 0
Regexp.last_match       #=> #<MatchData "cat" 1:"a">
Regexp.last_match(0)    #=> "cat"
Regexp.last_match(1)    #=> "a"
Regexp.last_match(2)    #=> nil

/(?<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"
new(string, [options]) → regexp Показать исходный код
new(regexp) → regexp
compile(string, [options]) → regexp
compile(regexp) → regexp
static VALUE
rb_reg_initialize_m(int argc, VALUE *argv, VALUE self)
{
    int flags = 0;
    VALUE str;
    rb_encoding *enc = 0;

    rb_check_arity(argc, 1, 3);
    if (RB_TYPE_P(argv[0], T_REGEXP)) {
        VALUE re = argv[0];

        if (argc > 1) {
            rb_warn("flags ignored");
        }
        rb_reg_check(re);
        flags = rb_reg_options(re);
        str = RREGEXP_SRC(re);
    }
    else {
        if (argc >= 2) {
            if (FIXNUM_P(argv[1])) flags = FIX2INT(argv[1]);
            else if (RTEST(argv[1])) flags = ONIG_OPTION_IGNORECASE;
        }
        if (argc == 3 && !NIL_P(argv[2])) {
            char *kcode = StringValuePtr(argv[2]);
            if (kcode[0] == 'n' || kcode[0] == 'N') {
                enc = rb_ascii8bit_encoding();
                flags |= ARG_ENCODING_NONE;
            }
            else {
                rb_warn("encoding option is ignored - %s", kcode);
            }
        }
        str = StringValue(argv[0]);
    }
    if (enc && rb_enc_get(str) != enc)
        rb_reg_init_str_enc(self, str, enc, flags);
    else
        rb_reg_init_str(self, str, flags);
    return self;
}

Создаёт новый регулярный выражения из pattern, который может быть либо строкой, либо объектом Regexp (в этом случае опции regexp будут распространены), и новые опции не могут быть указаны (изменение с версии Ruby 1.8).

Если options является целым числом, оно должно быть одним или несколькими из констант Regexp::EXTENDED, Regexp::IGNORECASE и Regexp::MULTILINE, объединённых оператором or. В противном случае, если options не nil или false, регулярное выражение будет регистронезависимым.

r1 = Regexp.new('^a-z+:\\s+\w+') #=> /^a-z+:\s+\w+/
r2 = Regexp.new('cat', true)     #=> /cat/i
r3 = Regexp.new(r2)              #=> /cat/i
r4 = Regexp.new('dog', Regexp::EXTENDED | Regexp::IGNORECASE) #=> /dog/ix
quote(str) → string Показать исходный код
static VALUE
rb_reg_s_quote(VALUE c, VALUE str)
{
    return rb_reg_quote(reg_operand(str, TRUE));
}

Экранирует любые символы, которые имели бы специальное значение в регулярном выражении. Возвращает новую экранированную строку или self, если нет символов для экранирования. Для любой строки, Regexp.new(Regexp.escape(str))=~str будет истинным.

Regexp.escape('\*?{}.')   #=> \\\*\?\{\}\.
try_convert(obj) → re or nil Показать исходный код
static VALUE
rb_reg_s_try_convert(VALUE dummy, VALUE re)
{
    return rb_check_regexp_type(re);
}

Попробуйте преобразовать obj в Regexp, используя метод to_regexp. Возвращает преобразованный regexp или nil, если obj не может быть преобразован по какой-либо причине.

Regexp.try_convert(/re/)         #=> /re/
Regexp.try_convert("re")         #=> nil

o = Object.new
Regexp.try_convert(o)            #=> nil
def o.to_regexp() /foo/ end
Regexp.try_convert(o)            #=> /foo/
union(pat1, pat2, ...) → new_regexp Показать исходный код
union(pats_ary) → new_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, который является объединением заданных patterns, т.е. будет соответствовать любой из его частей. Patterns могут быть объектами Regexp, в этом случае их опции будут сохранены, или строками. Если шаблоны не заданы, возвращает /(?!)/. Поведение не определено, если какой-либо заданный шаблон содержит захват.

Regexp.union                         #=> /(?!)/
Regexp.union("penzance")             #=> /penzance/
Regexp.union("a+b*c")                #=> /a\+b\*c/
Regexp.union("skiing", "sledding")   #=> /skiing|sledding/
Regexp.union(["skiing", "sledding"]) #=> /skiing|sledding/
Regexp.union(/dogs/, /cats/i)        #=> /(?-mix:dogs)|(?i-mx:cats)/

Примечание: аргументы для ::union будут пытаться преобразовать в регулярное выражение с помощью to_regexp.

Общедоступные методы экземпляра

rxp == other_rxp → true или false Показать исходный код
static 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;
    if (memcmp(RREGEXP_SRC_PTR(re1), RREGEXP_SRC_PTR(re2), RREGEXP_SRC_LEN(re1)) == 0) {
        return Qtrue;
    }
    return Qfalse;
}

Равенство — Два объекта regexp равны, если их шаблоны идентичны, у них одинаковый код набора символов, и их casefold? значения совпадают.

/abc/  == /abc/x   #=> false
/abc/  == /abc/i   #=> false
/abc/  == /abc/u   #=> false
/abc/u == /abc/n   #=> false
rxp === str → true или false Показать исходный код
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);
    if (start < 0) {
        return Qfalse;
    }
    return Qtrue;
}

Равенство с учётом регистра — используется в операторах case.

a = "HELLO"
case a
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"

Использование оператора === после литерала регулярного выражения позволяет сравнивать его с строкой.

/^[a-z]*$/ === "HELLO" #=> false
/^[A-Z]*$/ === "HELLO" #=> true
rxp =~ str → целое число или nil Показать исходный код
VALUE
rb_reg_match(VALUE re, VALUE str)
{
    long pos = reg_match_pos(re, &str, 0);
    if (pos < 0) return Qnil;
    pos = rb_str_sublen(str, pos);
    return LONG2FIX(pos);
}

Сопоставление — Сопоставляет rxp со строкой str.

/at/ =~ "input data"   #=> 7
/ax/ =~ "input data"   #=> nil

Если =~ используется с литералом regexp с именованными группами захвата, захваченные строки (или nil) присваиваются локальным переменным, именованным по именам групп захвата.

/(?<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

Это присваивание реализуется в парсере Ruby. Парсер распознаёт 'regexp-литерал =~ выражение' для присваивания. Regexp должен быть литералом без интерполяции и стоять слева от оператора.

Присваивание не происходит, если regexp не является литералом.

re = /(?<lhs>\w+)\s*=\s*(?<rhs>\w+)/
re =~ "  x = y  "
p lhs    # undefined local variable
p rhs    # undefined local variable

Интерполяция regexp, #{}, также отключает присваивание.

rhs_pat = /(?<rhs>\w+)/
/(?<lhs>\w+)\s*=\s*#{rhs_pat}/ =~ "x = y"
p lhs    # undefined local variable

Присваивание не происходит, если regexp стоит справа от оператора.

"  x = y  " =~ /(?<lhs>\w+)\s*=\s*(?<rhs>\w+)/
p lhs, rhs # 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);
    if (RREGEXP_PTR(re)->options & ONIG_OPTION_IGNORECASE) return Qtrue;
    return Qfalse;
}

Возвращает значение флага игнорирования регистра.

/a/.casefold?           #=> false
/a/i.casefold?          #=> true
/(?i:a)/.casefold?      #=> false
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?(other_rxp) → true или false Показать исходный код
static 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;
    if (memcmp(RREGEXP_SRC_PTR(re1), RREGEXP_SRC_PTR(re2), RREGEXP_SRC_LEN(re1)) == 0) {
        return Qtrue;
    }
    return Qfalse;
}

Равенство — Два объекта regexp равны, если их шаблоны идентичны, у них одинаковый код набора символов, и их casefold? значения совпадают.

/abc/  == /abc/x   #=> false
/abc/  == /abc/i   #=> false
/abc/  == /abc/u   #=> false
/abc/u == /abc/n   #=> false
fixed_encoding? → true или false Показать исходный код
static VALUE
rb_reg_fixed_encoding_p(VALUE re)
{
    if (FL_TEST(re, KCODE_FIXED))
        return Qtrue;
    else
        return Qfalse;
}

Возвращает false, если rxp применим к строке с любой кодировкой, совместимой с ASCII. В противном случае возвращает true.

r = /a/
r.fixed_encoding?                               #=> false
r =~ "\u{6666} a"                               #=> 2
r =~ "\xa1\xa2 a".force_encoding("euc-jp")      #=> 2
r =~ "abc".force_encoding("euc-jp")             #=> 0

r = /a/u
r.fixed_encoding?                               #=> true
r.encoding                                      #=> #<Encoding:UTF-8>
r =~ "\u{6666} a"                               #=> 2
r =~ "\xa1\xa2".force_encoding("euc-jp")        #=> Encoding::CompatibilityError
r =~ "abc".force_encoding("euc-jp")             #=> 0

r = /\u{6666}/
r.fixed_encoding?                               #=> true
r.encoding                                      #=> #<Encoding:UTF-8>
r =~ "\u{6666} a"                               #=> 0
r =~ "\xa1\xa2".force_encoding("euc-jp")        #=> Encoding::CompatibilityError
r =~ "abc".force_encoding("euc-jp")             #=> nil
hash → целое число Показать исходный код
static VALUE
rb_reg_hash(VALUE re)
{
    st_index_t hashval = reg_hash(re);
    return ST2FIX(hashval);
}

Вычисляет хеш-код на основе текста и опций этого регулярного выражения.

См. также 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);
}

Возвращает красиво отформатированную строку представления rxp. Удивительно, но #inspect фактически создаёт более естественную строку, чем #to_s.

/ab+c/ix.inspect        #=> "/ab+c/ix"
match(str) → matchdata или nil Показать исходный код
match(str,pos) → matchdata или nil
static VALUE
rb_reg_match_m(int argc, VALUE *argv, VALUE re)
{
    VALUE result, 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);
    if (pos < 0) {
        rb_backref_set(Qnil);
        return Qnil;
    }
    result = rb_backref_get();
    rb_match_busy(result);
    if (!NIL_P(result) && rb_block_given_p()) {
        return rb_yield(result);
    }
    return result;
}

Возвращает объект MatchData, описывающий совпадение, или nil, если совпадения не было. Это эквивалентно получению значения специальной переменной $~ после нормального сопоставления. Если второй параметр присутствует, он задаёт позицию в строке для начала поиска.

/(.)(.)(.)/.match("abc")[2]   #=> "b"
/(.)(.)/.match("abc", 1)[2]   #=> "c"

Если задан блок, вызывается блок с MatchData, если совпадение найдено, так что вы можете написать

/M(.*)/.match("Matz") do |m|
  puts m[0]
  puts m[1]
end

вместо

if m = /M(.*)/.match("Matz")
  puts m[0]
  puts m[1]
end

В этом случае возвращаемое значение — результат выполнения блока.

match?(str) → true или false Показать исходный код
match?(str,pos) → 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;
}

Возвращает хеш, представляющий информацию об именованных группах захвата rxp.

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

/(?<foo>.)(?<bar>.)/.named_captures
#=> {"foo"=>[1], "bar"=>[2]}

/(?<foo>.)(?<foo>.)/.named_captures
#=> {"foo"=>[1, 2]}

Если именованных групп захвата нет, возвращается пустой хеш.

/(?<foo>.)(?<bar>.)/.named_captures
#=> {"foo"=>[1], "bar"=>[2]}

/(?<foo>.)(?<foo>.)/.named_captures
#=> {"foo"=>[1, 2]}
names → [имя1, имя2, ...] Показать исходный код
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);
}

Возвращает набор битов, соответствующих опциям, используемым при создании этого объекта Regexp (см. Regexp::new для подробностей. Обратите внимание, что в возвращаемых значениях могут быть установлены дополнительные биты: они используются внутри кода регулярных выражений. Эти дополнительные биты игнорируются, если опции передаются в Regexp::new.

Regexp::IGNORECASE                  #=> 1
Regexp::EXTENDED                    #=> 2
Regexp::MULTILINE                   #=> 4

/cat/.options                       #=> 0
/cat/ix.options                     #=> 3
Regexp.new('cat', true).options     #=> 1
/\xa1\xa2/e.options                 #=> 16

r = /cat/ix
Regexp.new(r.source, r.options)     #=> /cat/ix
source → строка Показать исходный код
static VALUE
rb_reg_source(VALUE re)
{
    VALUE str;

    rb_reg_check(re);
    str = rb_str_dup(RREGEXP_SRC(re));
    if (OBJ_TAINTED(re)) OBJ_TAINT(str);
    return str;
}

Возвращает исходную строку шаблона.

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

Обратите внимание, что последовательности escape сохраняются как есть.

/\x20\+/.source  #=> "\\x20\\+"
to_json(*) Показать исходный код
# File ext/json/lib/json/add/regexp.rb, line 27
def to_json(*)
  as_json.to_json
end

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

to_s → строка Показать исходный код
static VALUE
rb_reg_to_s(VALUE re)
{
    int options, opt;
    const int embeddable = ONIG_OPTION_MULTILINE|ONIG_OPTION_IGNORECASE|ONIG_OPTION_EXTEND;
    long len;
    const UChar* ptr;
    VALUE str = rb_str_buf_new2("(?");
    char optbuf[5];
    rb_encoding *enc = rb_enc_get(re);

    rb_reg_check(re);

    rb_enc_copy(str, re);
    options = RREGEXP_PTR(re)->options;
    ptr = (UChar*)RREGEXP_SRC_PTR(re);
    len = RREGEXP_SRC_LEN(re);
  again:
    if (len >= 4 && ptr[0] == '(' && ptr[1] == '?') {
        int err = 1;
        ptr += 2;
        if ((len -= 2) > 0) {
            do {
                opt = char_to_option((int )*ptr);
                if (opt != 0) {
                    options |= opt;
                }
                else {
                    break;
                }
                ++ptr;
            } while (--len > 0);
        }
        if (len > 1 && *ptr == '-') {
            ++ptr;
            --len;
            do {
                opt = char_to_option((int )*ptr);
                if (opt != 0) {
                    options &= ~opt;
                }
                else {
                    break;
                }
                ++ptr;
            } while (--len > 0);
        }
        if (*ptr == ')') {
            --len;
            ++ptr;
            goto again;
        }
        if (*ptr == ':' && ptr[len-1] == ')') {
            Regexp *rp;
            VALUE verbose = ruby_verbose;
            ruby_verbose = Qfalse;

            ++ptr;
            len -= 2;
            err = onig_new(&rp, ptr, ptr + len, options,
                           enc, OnigDefaultSyntax, NULL);
            onig_free(rp);
            ruby_verbose = verbose;
        }
        if (err) {
            options = RREGEXP_PTR(re)->options;
            ptr = (UChar*)RREGEXP_SRC_PTR(re);
            len = RREGEXP_SRC_LEN(re);
        }
    }

    if (*option_to_str(optbuf, options)) rb_str_buf_cat2(str, optbuf);

    if ((options & embeddable) != embeddable) {
        optbuf[0] = '-';
        option_to_str(optbuf + 1, ~options);
        rb_str_buf_cat2(str, optbuf);
    }

    rb_str_buf_cat2(str, ":");
    if (rb_enc_asciicompat(enc)) {
        rb_reg_expr_str(str, (char*)ptr, len, enc, NULL);
        rb_str_buf_cat2(str, ")");
    }
    else {
        const char *s, *e;
        char *paren;
        ptrdiff_t n;
        rb_str_buf_cat2(str, ")");
        rb_enc_associate(str, rb_usascii_encoding());
        str = rb_str_encode(str, rb_enc_from_encoding(enc), 0, Qnil);

        /* backup encoded ")" to paren */
        s = RSTRING_PTR(str);
        e = RSTRING_END(str);
        s = rb_enc_left_char_head(s, e-1, e, enc);
        n = e - s;
        paren = ALLOCA_N(char, n);
        memcpy(paren, s, n);
        rb_str_resize(str, RSTRING_LEN(str) - n);

        rb_reg_expr_str(str, (char*)ptr, len, enc, NULL);
        rb_str_buf_cat(str, paren, n);
    }
    rb_enc_copy(str, re);

    OBJ_INFECT(str, re);
    return str;
}

Возвращает строку, содержащую регулярное выражение и его опции (используя обозначение (?opts:source)). Эта строка может быть передана обратно в Regexp::new для создания регулярного выражения с теми же семантиками, что и оригинал. (Однако, Regexp#== может не возвращать true при сравнении двух выражений, так как исходный код регулярного выражения может отличаться, как показано в примере). Regexp#inspect создаёт более читабельный вариант rxp.

r1 = /ab+c/ix           #=> /ab+c/ix
s1 = r1.to_s            #=> "(?ix-m:ab+c)"
r2 = Regexp.new(s1)     #=> /(?ix-m:ab+c)/
r1 == r2                #=> false
r1.source               #=> "ab+c"
r2.source               #=> "(?ix-m:ab+c)"
~ 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 с содержимым $_. Эквивалентно rxp =~ $_.

$_ = "input data"
~ /at/   #=> 7

Ruby Core © 1993–2017 Yukihiro Matsumoto
Licensed under the Ruby License.
Ruby Standard Library © contributors
Licensed under their own licenses.

Spec-Zone.ru

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