класс Regexp
A Regexp хранит регулярное выражение, используемое для сопоставления шаблона со строками. Регулярные выражения создаются с помощью литералов /.../ и %r{...} и конструктора Regexp::new.
Вы можете явно создать объект Regexp с помощью:
-
Литерала регулярного выражения.
Регулярные выражения (regexp) — это шаблоны, описывающие содержимое строки. Они используются для проверки наличия заданного шаблона в строке или извлечения совпадающих частей. Они создаются с помощью литералов /pat/ и %r{pat} или конструктора Regexp.new.
Regexp обычно задаётся с помощью обратных слэшей (/). Например:
/hay/ =~ 'haystack' #=> 0
/y/.match('haystack') #=> #<MatchData "y">
Если строка содержит шаблон, то говорят, что она соответствует шаблону. Литеральная строка соответствует самой себе.
Здесь строка «стог» не содержит шаблон «игла», поэтому она не соответствует ему:
/needle/.match('haystack') #=> nil
Здесь строка «стог» содержит шаблон «стог», поэтому она соответствует ему:
/hay/.match('haystack') #=> #<MatchData "hay">
В частности, /st/ требует, чтобы строка содержала букву s, за которой следует буква t, поэтому она соответствует строке стог.
=~ и 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 метод
Метод 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]/ означает 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]/ -
/\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:], newline, carriage return и т.д.) -
/[[:upper:]]/— Прописная буква -
/[[:xdigit:]]/— Цифра, допустимая в шестнадцатеричном числе (т.е. 0-9a-fA-F)
Ruby также поддерживает следующие не-POSIX классы символов:
-
/[[:word:]]/— Символ в одной из следующих категорий Unicode Буква, Диакритический знак, Число, Разделитель -
/[[: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 не может быть использована внутри regexp, так как обратные ссылки могут быть использованы только после окончания группы захвата, а обратная ссылка \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">
Примечание: regexp не может одновременно использовать именованные обратные ссылки и пронумерованные обратные ссылки. Также, если именованный захват используется в regexp, то скобки, используемые для группирования, которые в противном случае привели бы к безымянному захвату, обрабатываются как незахватывающие.
/(\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"}
Когда именованные группы захвата используются с буквальным regexp в левой части выражения и оператором =~, захваченный текст также присваивается локальным переменным с соответствующими именами.
/\$(?<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 обрабатывается как неделимое целое. Атомарное группирование обычно используется для оптимизации шаблонов, чтобы предотвратить излишнее возвращение к исходному положению (backtracking) механизма регулярных выражений.
" в шаблоне ниже соответствует первому символу строки, затем .* соответствует Quote“. Это приводит к тому, что общее сопоставление терпит неудачу, поэтому текст, сопоставленный с .* возвращается на одну позицию назад, что оставляет последний символ строки доступным для сопоставления "
/".*"/.match('"Quote"') #=> #<MatchData "\"Quote\"">
Если .* сгруппировано атомарно, оно отказывается возвращаться к исходному положению (backtracking) 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{Graph}/- Непробельный символ (исключает пробелы, управляющие символы и т.п.) -
/\p{Lower}/- Маленькая буква алфавита -
/\p{Print}/- Как\p{Graph}, но включает пробел -
/\p{Punct}/- Знак препинания -
/\p{Space}/- Пробельный символ ([:blank:], перевод строки, возврат каретки и т.д.) -
/\p{Upper}/- Большая буква алфавита -
/\p{XDigit}/- Цифра, допустимая в шестнадцатеричном числе (т.е., 0-9a-fA-F) -
/\p{Word}/- Член одной из следующих общих категорий Unicode: Буква, Знак, Число, Соединительный знак препинания -
/\p{ASCII}/- Символ набора символов ASCII -
/\p{Any}/- Любой символ Unicode (включая неназначенные символы) -
/\p{Assigned}/- Назначенный символ
Значение общей категории символа Unicode также может быть сопоставлено с \p{Ab}, где Ab — аббревиатура категории, как описано ниже:
-
/\p{L}/- ‘Буква’ -
/\p{Ll}/- ‘Буква: Строчная’ -
/\p{Lm}/- ‘Буква: Знак’ -
/\p{Lo}/- ‘Буква: Другое’ -
/\p{Lt}/- ‘Буква: Заглавная’ -
/\p{Lu}/- ‘Буква: Прописная’ -
/\p{Lo}/- ‘Буква: Другое’ -
/\p{M}/- ‘Знак’ -
/\p{Mn}/- ‘Знак: Непробельный’ -
/\p{Mc}/- ‘Знак: Пробельный, объединяющий’ -
/\p{Me}/- ‘Знак: Заключающий’ -
/\p{N}/- ‘Число’ -
/\p{Nd}/- ‘Число: Десятичная цифра’ -
/\p{Nl}/- ‘Число: Буква’ -
/\p{No}/- ‘Число: Другое’ -
/\p{P}/- ‘Знак препинания’ -
/\p{Pc}/- ‘Знак препинания: Соединительный’ -
/\p{Pd}/- ‘Знак препинания: Тире’ -
/\p{Ps}/- ‘Знак препинания: Открывающая’ -
/\p{Pe}/- ‘Знак препинания: Закрывающая’ -
/\p{Pi}/- ‘Знак препинания: Начальная кавычка’ -
/\p{Pf}/- ‘Знак препинания: Конечная кавычка’ -
/\p{Po}/- ‘Знак препинания: Другой’ -
/\p{S}/- ‘Символ’ -
/\p{Sm}/- ‘Символ: Математический’ -
/\p{Sc}/- ‘Символ: Валюта’ -
/\p{Sc}/- ‘Символ: Валюта’ -
/\p{Sk}/- ‘Символ: Модификатор’ -
/\p{So}/- ‘Символ: Другой’ -
/\p{Z}/- ‘Разделитель’ -
/\p{Zs}/- ‘Разделитель: Пробел’ -
/\p{Zl}/- ‘Разделитель: Строка’ -
/\p{Zp}/- ‘Разделитель: Абзац’ -
/\p{C}/- ‘Другое’ -
/\p{Cc}/- ‘Другое: Управляющий’ -
/\p{Cf}/- ‘Другое: Формат’ -
/\p{Cn}/- ‘Другое: Не назначено’ -
/\p{Co}/- ‘Другое: Для частного использования’ -
/\p{Cs}/- ‘Другое: Супплемент’
Наконец, \p{} соответствует Unicode скрипту символа. Поддерживаются следующие скрипты: арабский, армянский, балийский, бенгальский, бопомофо, брайлевский, бугинский, бухид, канадский_аборигенный, карийский, чамский, чероки, общий, коптский, клинописный, кипрский, кириллический, дезерет, деванагари, эфиопский, грузинский, глаголический, готический, греческий, гуджарати, гурумухи, хан, хангыль, хануноо, еврейский, хирагана, унаследованный, каннада, катакана, кайя_ли, харьошти, кхмерский, лаосский, латинский, лепча, лимбу, линейный_b, ликийский, лидийский, малаяламский, монгольский, мьянма, новый_тай_люй, нко, огам, ол_чики, древнеиталийский, древнеперсидский, орисса, османья, фагс_па, финикийский, реджанг, рунический, саурасhtra, шавиан, сингальский, сунданезский, силоти_нагри, сирийский, тагальский, тагбанва, тай_ле, тамильский, телугу, таана, тайский, тибетский, тифинаг, угаритский, вай и йи.
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- Соответствует не границам слов -
(?=шаблон)- Позитивное утверждение с опережением: гарантирует, что следующие символы соответствуют шаблону, но не включает эти символы в сопоставленный текст -
(?!шаблон)- Отрицательное утверждение с опережением: гарантирует, что следующие символы не соответствуют шаблону, но не включает эти символы в сопоставленный текст -
(?<=шаблон)- Позитивное утверждение с опережением: гарантирует, что предшествующие символы соответствуют шаблону, но не включает эти символы в сопоставленный текст -
(?<!шаблон)- Отрицательное утверждение с опережением: гарантирует, что предшествующие символы не соответствуют шаблону, но не включает эти символы в сопоставленный текст -
\K- Использует позитивное утверждение с опережением содержимого, предшествующего\Kв регулярном выражении. Например, следующие два регулярных выражения почти эквивалентны:/ab\Kc/ /(?<=ab)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, x и x также могут быть применены на уровне подвыражений с конструкцией (?вкл-выкл), которая включает параметры вкл и отключает параметры выкл для выражения, заключённого в скобки:
/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
Режим свободного форматирования и комментарии
Как упоминалось выше, параметр 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, с конструкцией (?#комментарий), где комментарий — произвольный текст, игнорируемый движком регулярных выражений.
Комментарии в литералах регулярных выражений не могут содержать неэкранированные символы-разделители.
Encoding
Предполагается, что регулярные выражения используют кодировку источника. Это можно переопределить с помощью одного из следующих модификаторов.
-
/шаблон/u- UTF-8 -
/шаблон/e- EUC-JP -
/шаблон/s- Windows-31J -
/шаблон/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.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
Константы
- 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));
} Экранирует любые символы, которые имели бы специальное значение в регулярном выражении. Возвращает новую экранированную строку с тем же или совместимым кодированием. Для любой строки, Regexp.new(Regexp.escape(str))=~str будет истинным.
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();
} Первый вариант возвращает объект 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"
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_category_warn(RB_WARN_CATEGORY_DEPRECATED, "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, которое может быть либо String, либо Regexp (в этом случае параметры regexp передаются), и новые параметры не могут быть указаны (изменение, начиная с Ruby 1.8).
Если options является Integer, это должно быть одно или несколько констант Regexp::EXTENDED, Regexp::IGNORECASE и Regexp::MULTILINE, объединённых побитовым ИЛИ. В противном случае, если options не nil или false, regexp будет регистронезависимым.
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
static VALUE
rb_reg_s_quote(VALUE c, VALUE str)
{
return rb_reg_quote(reg_operand(str, TRUE));
} Экранирует любые символы, которые имели бы специальное значение в регулярном выражении. Возвращает новую экранированную строку с тем же или совместимым кодированием. Для любой строки, Regexp.new(Regexp.escape(str))=~str будет истинным.
Regexp.escape('\*?{}.') #=> \\\*\?\{\}\.
static VALUE
rb_reg_s_try_convert(VALUE dummy, VALUE re)
{
return rb_check_regexp_type(re);
} Попытка преобразовать объект в Regexp, используя метод to_regexp. Возвращает преобразованное regexp или nil, если объект не может быть преобразован по какой-либо причине.
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/
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, который является объединением заданных шаблонов, т. е. будет соответствовать любому из его частей. Шаблоны могут быть объектами 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.
Открытые методы экземпляра
Равенство—Два регулярных выражения равны, если их шаблоны идентичны, у них один и тот же код кодировки символов, и их значения casefold? одинаковы.
/abc/ == /abc/x #=> false /abc/ == /abc/i #=> false /abc/ == /abc/u #=> false /abc/u == /abc/n #=> false
static VALUE
rb_reg_eqq(VALUE re, VALUE str)
{
long start;
str = reg_operand(str, FALSE);
if (NIL_P(str)) {
rb_backref_set(Qnil);
return Qfalse;
}
start = rb_reg_search(re, str, 0, 0);
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"
Использование после литерала регулярного выражения оператора === позволяет сравнивать его с String.
/^[a-z]*$/ === "HELLO" #=> false /^[A-Z]*$/ === "HELLO" #=> true
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);
} Сопоставление—Сопоставляет rxp с str.
/at/ =~ "input data" #=> 7 /ax/ =~ "input data" #=> nil
Если =~ используется с литералом регулярного выражения с именованными захватчиками, захваченные строки (или 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. Парсер обнаруживает «литерал-регулярного-выражения =~ выражение» для присваивания. Регулярное выражение должно быть литералом без интерполяции и размещаться в левой части.
Присваивание не происходит, если регулярное выражение не является литералом.
re = /(?<lhs>\w+)\s*=\s*(?<rhs>\w+)/ re =~ " x = y " p lhs # undefined local variable p rhs # undefined local variable
Интерполяция регулярного выражения, #{}, также отключает присваивание.
rhs_pat = /(?<rhs>\w+)/
/(?<lhs>\w+)\s*=\s*#{rhs_pat}/ =~ "x = y"
p lhs # undefined local variable
Присваивание не происходит, если регулярное выражение находится в правой части.
" x = y " =~ /(?<lhs>\w+)\s*=\s*(?<rhs>\w+)/ p lhs, rhs # 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);
} Возвращает значение флага без учёта регистра.
/a/.casefold? #=> false /a/i.casefold? #=> true /(?i:a)/.casefold? #=> false
VALUE
rb_obj_encoding(VALUE obj)
{
int idx = rb_enc_get_index(obj);
if (idx < 0) {
rb_raise(rb_eTypeError, "unknown encoding");
}
return rb_enc_from_encoding_index(idx & ENC_INDEX_MASK);
} Возвращает объект Encoding, представляющий кодировку obj.
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);
} Равенство—Два регулярных выражения равны, если их шаблоны идентичны, у них один и тот же код кодировки символов, и их значения casefold? одинаковы.
/abc/ == /abc/x #=> false /abc/ == /abc/i #=> false /abc/ == /abc/u #=> false /abc/u == /abc/n #=> false
static VALUE
rb_reg_fixed_encoding_p(VALUE re)
{
return RBOOL(FL_TEST(re, KCODE_FIXED));
} Возвращает 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
VALUE
rb_reg_hash(VALUE re)
{
st_index_t hashval = reg_hash(re);
return ST2FIX(hashval);
} Создаёт хеш на основе текста и параметров этого регулярного выражения.
См. также 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);
} Создаёт хорошо отформатированную строковую версию rxp. Возможно, удивительно, но #inspect фактически создаёт более естественную версию строки, чем #to_s.
/ab+c/ix.inspect #=> "/ab+c/ix"
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, если совпадение не найдено. Это эквивалентно получению значения специальной переменной $~ после обычного совпадения. Если присутствует второй параметр, он указывает позицию в строке, с которой следует начать поиск.
/(.)(.)(.)/.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
В этом случае возвращаемым значением является значение из выполнения блока.
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;
} Возвращает хеш, представляющий информацию об именованных захватчиках rxp.
Ключом хеша является имя именованных захватчиков. Значением хеша является массив, представляющий собой список индексов соответствующих именованных захватчиков.
/(?<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);
} Возвращает набор битов, соответствующих параметрам, используемым при создании этого 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
static VALUE
rb_reg_source(VALUE re)
{
VALUE str;
rb_reg_check(re);
str = rb_str_dup(RREGEXP_SRC(re));
return str;
} Возвращает исходную строку шаблона.
/ab+c/ix.source #=> "ab+c"
Обратите внимание, что escape-последовательности сохраняются как есть.
/\x20\+/.source #=> "\\x20\\+"
static VALUE
rb_reg_to_s(VALUE re)
{
return rb_reg_str_with_term(re, '/');
} Возвращает строку, содержащую регулярное выражение и его параметры (с использованием обозначения (?opts:source). Эту строку можно снова передать в Regexp::new для создания регулярного выражения с той же семантикой, что и у оригинала. (Однако Regexp#== может возвращать false при сравнении двух выражений, поскольку сам источник регулярного выражения может отличаться, как показано в примере). 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)"
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–2022 Yukihiro Matsumoto
Licensed under the Ruby License.
Ruby Standard Library © contributors
Licensed under their own licenses.