класс 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
-
Совпадает в начале строки, т.е. перед первым символом.
-
Входит в именованную группу захвата под названием
paren -
Совпадает с литеральным символом (, первым символом в строке
-
Вызывает группу
parenснова, т.е. рекурсивно возвращается к второму шагу -
Повторно входит в группу
paren -
Совпадает с литеральным символом (, вторым символом в строке
-
Попытка вызвать
parenв третий раз, но терпит неудачу, потому что это помешает общему успешному совпадению -
Совпадает с литеральным символом ), третьим символом в строке. Помечает конец второго рекурсивного вызова
-
Совпадает с литеральным символом ), четвёртым символом в строке
-
Совпадает с концом строки
Альтернация
Метасимвол вертикальной черты (|) объединяет несколько выражений в одно, которое соответствует любому из них. Каждое выражение является альтернативой.
/\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’
Наконец, \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
Методы публичного класса
Псевдоним для Regexp.new
static VALUE
rb_reg_s_quote(VALUE c, VALUE str)
{
return rb_reg_quote(reg_operand(str, TRUE));
} Возвращает новую строку, в которой экранированы все символы, имеющие специальное значение в регулярном выражении:
s = Regexp.escape('\*?{}.') # => "\\\\\\*\\?\\{\\}\\."
Для любой строки s, этот вызов возвращает объект MatchData:
r = Regexp.new(Regexp.escape(s)) # => /\\\\\\\*\\\?\\\{\\\}\\\./
r.match(s) # => #<MatchData "\\\\\\*\\?\\{\\}\\.">
Regexp.quote является псевдонимом для Regexp.escape.
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.
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 для всего.
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.
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.
static VALUE
rb_reg_s_timeout_get(VALUE dummy)
{
double d = hrtime2double(rb_reg_match_time_limit);
if (d == 0.0) return Qnil;
return DBL2NUM(d);
} Возвращает текущий интервал таймаута по умолчанию для сопоставлений Regexp в секундах. nil означает отсутствие конфигурации таймаута по умолчанию.
static VALUE
rb_reg_s_timeout_set(VALUE dummy, VALUE timeout)
{
rb_ractor_ensure_main_ractor("can not access Regexp.timeout from non-main Ractors");
set_timeout(&rb_reg_match_time_limit, timeout);
return timeout;
} Устанавливает интервал таймаута по умолчанию для сопоставлений Regexp в секундах. nil означает отсутствие конфигурации таймаута по умолчанию. Эта конфигурация глобальна для процесса. Если вам нужно задать таймаут для каждого Regexp, используйте ключевой аргумент timeout для Regexp.new.
Regexp.timeout = 1 /^a*b?a*$/ =~ "a" * 100000 + "x" #=> regexp match timeout (RuntimeError)
static VALUE
rb_reg_s_try_convert(VALUE dummy, VALUE re)
{
return rb_check_regexp_type(re);
} Возвращает object в случае, если это regexp:
Regexp.try_convert(/re/) # => /re/
В противном случае, если object отвечает на :to_regexp, вызывает object.to_regexp и возвращает результат.
Возвращает nil если object не отвечает на :to_regexp.
Regexp.try_convert('re') # => nil
Вызывает исключение, если object.to_regexp не возвращает regexp.
static VALUE
rb_reg_s_union_m(VALUE self, VALUE args)
{
VALUE v;
if (RARRAY_LEN(args) == 1 &&
!NIL_P(v = rb_check_array_type(rb_ary_entry(args, 0)))) {
return rb_reg_s_union(self, v);
}
return rb_reg_s_union(self, args);
} Возвращает новый regexp, который является объединением заданных шаблонов:
r = Regexp.union(%w[cat dog]) # => /cat|dog/
r.match('cat') # => #<MatchData "cat">
r.match('dog') # => #<MatchData "dog">
r.match('cog') # => nil
Для каждого шаблона, который является строкой, используется Regexp.new(pattern):
Regexp.union('penzance') # => /penzance/
Regexp.union('a+b*c') # => /a\+b\*c/
Regexp.union('skiing', 'sledding') # => /skiing|sledding/
Regexp.union(['skiing', 'sledding']) # => /skiing|sledding/
Для каждого шаблона, который является regexp, он используется как есть, включая его флаги:
Regexp.union(/foo/i, /bar/m, /baz/x) # => /(?i-mx:foo)|(?m-ix:bar)|(?x-mi:baz)/ Regexp.union([/foo/i, /bar/m, /baz/x]) # => /(?i-mx:foo)|(?m-ix:bar)|(?x-mi:baz)/
Без аргументов, возвращает /(?!)/:
Regexp.union # => /(?!)/
Если какой-либо шаблон regexp содержит захваты, поведение не определено.
Методы экземпляров публичного интерфейса
Возвращает true, если object является другим объектом Regexp, у которого шаблон, флаги и кодировка совпадают с self, в противном случае false:
/foo/ == Regexp.new('foo') # => true
/foo/ == /foo/i # => false
/foo/ == Regexp.new('food') # => false
/foo/ == Regexp.new("abc".force_encoding("euc-jp")) # => false
Regexp#eql? — псевдоним для Regexp#==.
static VALUE
rb_reg_eqq(VALUE re, VALUE str)
{
long start;
str = reg_operand(str, FALSE);
if (NIL_P(str)) {
rb_backref_set(Qnil);
return Qfalse;
}
start = rb_reg_search(re, str, 0, 0);
return RBOOL(start >= 0);
} Возвращает true, если self находит совпадение в string:
/^[a-z]*$/ === 'HELLO' # => false /^[A-Z]*$/ === 'HELLO' # => true
Этот метод используется в операторах case:
s = 'HELLO' case s when /\A[a-z]*\z/; print "Lower case\n" when /\A[A-Z]*\z/; print "Upper case\n" else print "Mixed case\n" end # => "Upper case"
VALUE
rb_reg_match(VALUE re, VALUE str)
{
long pos = reg_match_pos(re, &str, 0, NULL);
if (pos < 0) return Qnil;
pos = rb_str_sublen(str, pos);
return LONG2FIX(pos);
} Возвращает целочисленный индекс (в символах) первого совпадения для self и string, или nil в случае отсутствия совпадения; также устанавливает глобальные переменные Regexp:
/at/ =~ 'input data' # => 7 $~ # => #<MatchData "at"> /ax/ =~ 'input data' # => nil $~ # => nil
Присваивает именованные группы локальным переменным с соответствующими именами только в том случае, если self:
-
Это литерал регулярного выражения; см. Литералы регулярных выражений.
-
Не содержит интерполяций; см. Интерполяция регулярных выражений.
-
Находится слева от выражения.
Пример:
/(?<lhs>\w+)\s*=\s*(?<rhs>\w+)/ =~ ' x = y ' p lhs # => "x" p rhs # => "y"
Присваивает nil в случае отсутствия совпадения:
/(?<lhs>\w+)\s*=\s*(?<rhs>\w+)/ =~ ' x = ' p lhs # => nil p rhs # => nil
Не выполняет присвоение локальных переменных, если self не является литералом регулярного выражения:
r = /(?<foo>\w+)\s*=\s*(?<foo>\w+)/ r =~ ' x = y ' p foo # Undefined local variable p bar # Undefined local variable
Присвоение не выполняется, если регулярное выражение не находится слева:
' x = y ' =~ /(?<foo>\w+)\s*=\s*(?<foo>\w+)/ p foo, foo # Undefined local variables
Интерполяция регулярного выражения, #{}, также отключает присвоение:
r = /(?<foo>\w+)/
/(?<foo>\w+)\s*=\s*#{r}/ =~ 'x = y'
p foo # Undefined local variable
# 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 и представлять этот объект.
static VALUE
rb_reg_casefold_p(VALUE re)
{
rb_reg_check(re);
return RBOOL(RREGEXP_PTR(re)->options & ONIG_OPTION_IGNORECASE);
} Возвращает true, если флаг игнорирования регистра в self установлен, в противном случае false:
/a/.casefold? # => false /a/i.casefold? # => true /(?i:a)/.casefold? # => false
VALUE
rb_obj_encoding(VALUE obj)
{
int idx = rb_enc_get_index(obj);
if (idx < 0) {
rb_raise(rb_eTypeError, "unknown encoding");
}
return rb_enc_from_encoding_index(idx & ENC_INDEX_MASK);
} Возвращает объект Encoding, представляющий кодировку объекта.
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#==.
static VALUE
rb_reg_fixed_encoding_p(VALUE re)
{
return RBOOL(FL_TEST(re, KCODE_FIXED));
} Возвращает false, если self применим к строке с любой кодировкой, совместимой с ASCII, в противном случае true:
r = /a/ # => /a/
r.fixed_encoding? # => false
r.match?("\u{6666} a") # => true
r.match?("\xa1\xa2 a".force_encoding("euc-jp")) # => true
r.match?("abc".force_encoding("euc-jp")) # => true
r = /a/u # => /a/
r.fixed_encoding? # => true
r.match?("\u{6666} a") # => true
r.match?("\xa1\xa2".force_encoding("euc-jp")) # Raises exception.
r.match?("abc".force_encoding("euc-jp")) # => true
r = /\u{6666}/ # => /\u{6666}/
r.fixed_encoding? # => true
r.encoding # => #<Encoding:UTF-8>
r.match?("\u{6666} a") # => true
r.match?("\xa1\xa2".force_encoding("euc-jp")) # Raises exception.
r.match?("abc".force_encoding("euc-jp")) # => false
VALUE
rb_reg_hash(VALUE re)
{
st_index_t hashval = reg_hash(re);
return ST2FIX(hashval);
} Возвращает целочисленное значение хеша для self.
Связанно с: Object#hash.
static VALUE
rb_reg_inspect(VALUE re)
{
if (!RREGEXP_PTR(re) || !RREGEXP_SRC(re) || !RREGEXP_SRC_PTR(re)) {
return rb_any_to_s(re);
}
return rb_reg_desc(RREGEXP_SRC_PTR(re), RREGEXP_SRC_LEN(re), re);
} Возвращает красиво отформатированное строковое представление self:
/ab+c/ix.inspect # => "/ab+c/ix"
Связанно с: Regexp#to_s.
static VALUE
rb_reg_match_m(int argc, VALUE *argv, VALUE re)
{
VALUE result = Qnil, str, initpos;
long pos;
if (rb_scan_args(argc, argv, "11", &str, &initpos) == 2) {
pos = NUM2LONG(initpos);
}
else {
pos = 0;
}
pos = reg_match_pos(re, &str, pos, &result);
if (pos < 0) {
rb_backref_set(Qnil);
return Qnil;
}
rb_match_busy(result);
if (!NIL_P(result) && rb_block_given_p()) {
return rb_yield(result);
}
return result;
} Без блока возвращает объект MatchData, описывающий совпадение (если оно есть), или nil в противном случае; поиск начинается с указанного символа offset в string:
/abra/.match('abracadabra') # => #<MatchData "abra">
/abra/.match('abracadabra', 4) # => #<MatchData "abra">
/abra/.match('abracadabra', 8) # => nil
/abra/.match('abracadabra', 800) # => nil
string = "\u{5d0 5d1 5e8 5d0}cadabra"
/abra/.match(string, 7) #=> #<MatchData "abra">
/abra/.match(string, 8) #=> nil
/abra/.match(string.b, 8) #=> #<MatchData "abra">
С блоком вызывает блок только в случае совпадения; возвращает значение блока:
/abra/.match('abracadabra') {|matchdata| p matchdata }
# => #<MatchData "abra">
/abra/.match('abracadabra', 4) {|matchdata| p matchdata }
# => #<MatchData "abra">
/abra/.match('abracadabra', 8) {|matchdata| p matchdata }
# => nil
/abra/.match('abracadabra', 8) {|marchdata| fail 'Cannot happen' }
# => nil
Вывод (из первых двух блоков выше):
#<MatchData "abra">
#<MatchData "abra">
/(.)(.)(.)/.match("abc")[2] # => "b"
/(.)(.)/.match("abc", 1)[2] # => "c"
static VALUE
rb_reg_match_m_p(int argc, VALUE *argv, VALUE re)
{
long pos = rb_check_arity(argc, 1, 2) > 1 ? NUM2LONG(argv[1]) : 0;
return rb_reg_match_p(re, argv[0], pos);
} Возвращает true или false для указания, соответствует ли регулярное выражение или нет, без обновления $~ и других связанных переменных. Если второй параметр присутствует, он задаёт позицию в строке для начала поиска.
/R.../.match?("Ruby") # => true
/R.../.match?("Ruby", 1) # => false
/P.../.match?("Ruby") # => false
$& # => nil
static VALUE
rb_reg_named_captures(VALUE re)
{
regex_t *reg = (rb_reg_check(re), RREGEXP_PTR(re));
VALUE hash = rb_hash_new_with_size(onig_number_of_names(reg));
onig_foreach_name(reg, reg_named_captures_iter, (void*)hash);
return hash;
} Возвращает хеш, представляющий именованные группы self (см. Именованные группы):
-
Каждый ключ — имя именованной группы.
-
Каждый значение — массив целочисленных индексов для этой именованной группы.
Примеры:
/(?<foo>.)(?<bar>.)/.named_captures # => {"foo"=>[1], "bar"=>[2]}
/(?<foo>.)(?<foo>.)/.named_captures # => {"foo"=>[1, 2]}
/(.)(.)/.named_captures # => {}
static VALUE
rb_reg_names(VALUE re)
{
VALUE ary;
rb_reg_check(re);
ary = rb_ary_new_capa(onig_number_of_names(RREGEXP_PTR(re)));
onig_foreach_name(RREGEXP_PTR(re), reg_names_iter, (void*)ary);
return ary;
} Возвращает массив имён групп (см. Именованные группы):
/(?<foo>.)(?<bar>.)(?<baz>.)/.names # => ["foo", "bar", "baz"] /(?<foo>.)(?<foo>.)/.names # => ["foo"] /(.)(.)/.names # => []
static VALUE
rb_reg_options_m(VALUE re)
{
int options = rb_reg_options(re);
return INT2NUM(options);
} Возвращает целое число, биты которого отображают установленные параметры в self.
Биты параметров:
Regexp::IGNORECASE # => 1 Regexp::EXTENDED # => 2 Regexp::MULTILINE # => 4
Примеры:
/foo/.options # => 0 /foo/i.options # => 1 /foo/x.options # => 2 /foo/m.options # => 4 /foo/mix.options # => 7
Обратите внимание, что в возвращаемом целом числе могут быть установлены дополнительные биты; они хранятся внутри self, игнорируются при передаче в Regexp.new и могут игнорироваться вызывающим кодом:
Возвращает набор битов, соответствующих параметрам, использованным при создании этого регулярного выражения (см. Regexp::new для подробностей). Обратите внимание, что в возвращаемых параметрах могут быть установлены дополнительные биты: они используются внутренним кодом регулярных выражений. Эти дополнительные биты игнорируются, если параметры передаются в Regexp::new:
r = /\xa1\xa2/e # => /\xa1\xa2/ r.source # => "\\xa1\\xa2" r.options # => 16 Regexp.new(r.source, r.options) # => /\xa1\xa2/
static VALUE
rb_reg_source(VALUE re)
{
VALUE str;
rb_reg_check(re);
str = rb_str_dup(RREGEXP_SRC(re));
return str;
} Возвращает исходную строку self:
/ab+c/ix.source # => "ab+c"
Regexp последовательности экранирования сохраняются:
/\x20\+/.source # => "\\x20\\+"
Символы экранирования лексического анализатора не сохраняются:
/\//.source # => "/"
static VALUE
rb_reg_timeout_get(VALUE re)
{
rb_reg_check(re);
double d = hrtime2double(RREGEXP_PTR(re)->timelimit);
if (d == 0.0) return Qnil;
return DBL2NUM(d);
} Возвращает интервал таймаута для Regexp сопоставления во секундах. nil означает отсутствие настройки таймаута по умолчанию.
Эта настройка применяется к каждому объекту. Глобальная настройка, заданная с помощью Regexp.timeout=, игнорируется, если задана настройка для каждого объекта.
re = Regexp.new("^a*b?a*$", timeout: 1)
re.timeout #=> 1.0
re =~ "a" * 100000 + "x" #=> regexp match timeout (RuntimeError)
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.
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.