Spec-Zone.ru › Ruby 3.3

класс Regexp

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

Регулярное выражение (также называемое regexp) является шаблоном соответствия (также просто шаблоном).

Обычная запись regexp использует обрамляющие слеши:

/foo/

Regexp может быть применён к целевой строке; часть строки (если такая есть), которая соответствует шаблону, называется совпадением, и может быть названа соответствующей:

re = /red/
re.match?('redirect') # => true   # Match at beginning of target.
re.match?('bored')    # => true   # Match at end of target.
re.match?('credit')   # => true   # Match within target.
re.match?('foo')      # => false  # No match.

Применение Regexp

Regexp может быть использован:

  • Для извлечения подстрок на основе заданного шаблона:

    re = /foo/              # => /foo/
    re.match('food')        # => #<MatchData "foo">
    re.match('good')        # => nil
    

    См. разделы Метод match и Оператор =~.

  • Для определения того, соответствует ли строка заданному шаблону:

    re.match?('food') # => true
    re.match?('good') # => false
    

    См. раздел Метод match?.

  • В качестве аргумента для вызовов определённых методов в других классах и модулях; большинство таких методов принимают аргумент, который может быть либо строкой, либо (значительно более мощным) regexp.

    См. Методы Regexp.

Объекты Regexp

Объект regexp имеет:

  • Источник; см. Источники.

  • Несколько режимов; см. Режимы.

  • Таймаут; см. Таймауты.

  • Кодировку; см. Кодировки.

Создание Regexp

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

  • Литерала regexp, использующего слеши (см. Литералы Regexp):

    # This is a very common usage.
    /foo/ # => /foo/
    
  • Литерала regexp (см. %r: Литералы Regexp):

    # Same delimiter character at beginning and end;
    # useful for avoiding escaping characters
    %r/name\/value pair/ # => /name\/value pair/
    %r:name/value pair:  # => /name\/value pair/
    %r|name/value pair|  # => /name\/value pair/
    
    # Certain "paired" characters can be delimiters.
    %r[foo] # => /foo/
    %r{foo} # => /foo/
    %r(foo) # => /foo/
    %r<foo> # => /foo/
    
  • Метод Regexp.new.

Метод match

Каждый из методов Regexp#match, String#match и Symbol#match возвращает объект MatchData, если совпадение найдено, nil в противном случае; каждый также устанавливает глобальные переменные:

'food'.match(/foo/) # => #<MatchData "foo">
'food'.match(/bar/) # => nil

Оператор =~

Каждый из операторов Regexp#=~, String#=~ и Symbol#=~ возвращает целочисленное смещение, если совпадение найдено, nil в противном случае; каждый также устанавливает глобальные переменные:

/bar/ =~ 'foo bar' # => 4
'foo bar' =~ /bar/ # => 4
/baz/ =~ 'foo bar' # => nil

Метод match?

Каждый из методов Regexp#match?, String#match? и Symbol#match? возвращает true , если совпадение найдено, false в противном случае; ни один не устанавливает глобальные переменные:

'food'.match?(/foo/) # => true
'food'.match?(/bar/) # => false

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

Определённые методы, ориентированные на regexp, присваивают значения глобальным переменным:

  • #match: см. Метод match.

  • #=~: см. Оператор =~.

Затронутые глобальные переменные:

  • $~: Возвращает объект MatchData или nil.

  • $&: Возвращает сопоставленную часть строки или nil.

  • $`: Возвращает часть строки слева от совпадения или nil.

  • $': Возвращает часть строки справа от совпадения или nil.

  • $+: Возвращает последнюю сопоставленную группу или nil.

  • $1, $2, и т.д.: Возвращает первую, вторую и т.д. сопоставленные группы или nil. Обратите внимание, что $0 сильно отличается; она возвращает имя текущей выполняемой программы.

Примеры:

# Matched string, but no matched groups.
'foo bar bar baz'.match('bar')
$~ # => #<MatchData "bar">
$& # => "bar"
$` # => "foo "
$' # => " bar baz"
$+ # => nil
$1 # => nil

# Matched groups.
/s(\w{2}).*(c)/.match('haystack')
$~ # => #<MatchData "stac" 1:"ta" 2:"c">
$& # => "stac"
$` # => "hay"
$' # => "k"
$+ # => "c"
$1 # => "ta"
$2 # => "c"
$3 # => nil

# No match.
'foo'.match('bar')
$~ # => nil
$& # => nil
$` # => nil
$' # => nil
$+ # => nil
$1 # => nil

Обратите внимание, что Regexp#match?, String#match? и Symbol#match? не устанавливают глобальные переменные.

Источники

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

re = /foo/              # => /foo/
re.match('food')        # => #<MatchData "foo">
re.match('good')        # => nil

Богатый набор доступных подвыражений придаёт regexp большую мощность и гибкость:

  • Специальные символы

  • Литералы источника

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

  • Кратко обозначенные классы символов

  • Якоря

  • Альтернация

  • Квантификаторы

  • Группы и захват

  • Unicode

  • POSIX скобочные выражения

  • Комментарии

Специальные символы

Специальные символы regexp, называемые метасимволами, имеют особое значение в некоторых контекстах; в зависимости от контекста, они иногда являются метасимволами:

. ? - + * ^ \ | $ ( ) [ ] { }

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

# Matches one or more 'o' characters.
/o+/.match('foo')  # => #<MatchData "oo">
# Would match 'o+'.
/o\+/.match('foo') # => nil

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

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

Method Regexp.escape возвращает экранированную строку:

Regexp.escape('.?-+*^\|$()[]{}')
# => "\\.\\?\\-\\+\\*\\^\\\\\\|\\$\\(\\)\\[\\]\\{\\}"

Литералы источника

Литерал источника в значительной степени ведет себя как строка в двойных кавычках; см. Строковые литералы.

В частности, литерал источника может содержать интерполированные выражения:

s = 'foo'         # => "foo"
/#{s}/            # => /foo/
/#{s.capitalize}/ # => /Foo/
/#{2 + 2}/        # => /4/

Существуют различия между обычным строковым литералом и литералом источника; см. Кратко обозначенные классы символов.

  • \s в обычном строковом литерале эквивалентен пробелу; в литерале источника это сокращение для соответствия символу пробела.

  • В обычном строковом литерале это (лишённые смысла) экранированные символы; в литерале источника они являются сокращениями для различных символов соответствия:

    \w \W \d \D \h \H \S \R

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

Класс символов ограничен квадратными скобками; он определяет, что определённые символы соответствуют в данной точке целевой строки:

# This character class will match any vowel.
re = /B[aeiou]rd/
re.match('Bird') # => #<MatchData "Bird">
re.match('Bard') # => #<MatchData "Bard">
re.match('Byrd') # => nil

Класс символов может содержать дефисы, чтобы указать диапазоны символов:

# These regexps have the same effect.
/[abcdef]/.match('foo') # => #<MatchData "f">
/[a-f]/.match('foo')    # => #<MatchData "f">
/[a-cd-f]/.match('foo') # => #<MatchData "f">

Когда первым символом класса символов является символ возведения в степень (^), смысл класса инвертируется: он соответствует любому символу кроме указанных.

/[^a-eg-z]/.match('f') # => #<MatchData "f">

Класс символов может содержать другой класс символов. Само по себе это не имеет значения, потому что [a-z[0-9]] описывает тот же набор, что и [a-z0-9].

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

/[a-w&&[^c-g]z]/ # ([a-w] AND ([^c-g] OR z))

Это эквивалентно:

/[abh-w]/

Кратко обозначенные классы символов

Каждый из следующих метасимволов служит сокращением для класса символов:

  • /./: Соответствует любому символу, кроме новой строки:

    /./.match('foo') # => #<MatchData "f">
    /./.match("\n")  # => nil
    
  • /./m: Соответствует любому символу, включая новую строку; см. Режим многострочного совпадения:

    /./m.match("\n") # => #<MatchData "\n">
    
  • /\w/: Соответствует символу слова; эквивалентно [a-zA-Z0-9_]:

    /\w/.match(' foo') # => #<MatchData "f">
    /\w/.match(' _')   # => #<MatchData "_">
    /\w/.match(' ')    # => nil
    
  • /\W/: Соответствует символу, не являющемуся словом: эквивалентно [^a-zA-Z0-9_]:

    /\W/.match(' ') # => #<MatchData " ">
    /\W/.match('_') # => nil
    
  • /\d/: Соответствует цифровому символу: эквивалентно [0-9]:

    /\d/.match('THX1138') # => #<MatchData "1">
    /\d/.match('foo')     # => nil
    
  • /\D/: Соответствует символу, не являющемуся цифрой: эквивалентно [^0-9]:

    /\D/.match('123Jump!') # => #<MatchData "J">
    /\D/.match('123')      # => nil
    
  • /\h/: Соответствует символу шестнадцатеричной цифры: эквивалентно [0-9a-fA-F]:

    /\h/.match('xyz fedcba9876543210') # => #<MatchData "f">
    /\h/.match('xyz')                  # => nil
    
  • /\H/: Соответствует символу, не являющемуся шестнадцатеричной цифрой: эквивалентно [^0-9a-fA-F]:

    /\H/.match('fedcba9876543210xyz') # => #<MatchData "x">
    /\H/.match('fedcba9876543210')    # => nil
    
  • /\s/: Соответствует символу пробела: эквивалентно /[ \t\r\n\f\v]/:

    /\s/.match('foo bar') # => #<MatchData " ">
    /\s/.match('foo')     # => nil
    
  • /\S/: Соответствует символу, не являющемуся пробелом: эквивалентно /[^ \t\r\n\f\v]/:

    /\S/.match(" \t\r\n\f\v foo") # => #<MatchData "f">
    /\S/.match(" \t\r\n\f\v")     # => nil
    
  • /\R/: Соответствует разрыву строки независимо от платформы:

    /\R/.match("\r")     # => #<MatchData "\r">     # Carriage return (CR)
    /\R/.match("\n")     # => #<MatchData "\n">     # Newline (LF)
    /\R/.match("\f")     # => #<MatchData "\f">     # Formfeed (FF)
    /\R/.match("\v")     # => #<MatchData "\v">     # Vertical tab (VT)
    /\R/.match("\r\n")   # => #<MatchData "\r\n">   # CRLF
    /\R/.match("\u0085") # => #<MatchData "\u0085"> # Next line (NEL)
    /\R/.match("\u2028") # => #<MatchData "\u2028"> # Line separator (LSEP)
    /\R/.match("\u2029") # => #<MatchData "\u2029"> # Paragraph separator (PSEP)
    

Якоря

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

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

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

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

Граничные якоря

Каждый из этих якорей соответствует границе:

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

    /^bar/.match("foo\nbar") # => #<MatchData "bar">
    /^ar/.match("foo\nbar")  # => nil
    
  • $: Соответствует концу строки:

    /bar$/.match("foo\nbar") # => #<MatchData "bar">
    /ba$/.match("foo\nbar")  # => nil
    
  • \A: Соответствует началу строки:

    /\Afoo/.match('foo bar')  # => #<MatchData "foo">
    /\Afoo/.match(' foo bar') # => nil
    
  • \Z: Соответствует концу строки; если строка заканчивается единственной новой строкой, оно соответствует символу перед завершающей новой строкой:

    /foo\Z/.match('bar foo')     # => #<MatchData "foo">
    /foo\Z/.match('foo bar')     # => nil
    /foo\Z/.match("bar foo\n")   # => #<MatchData "foo">
    /foo\Z/.match("bar foo\n\n") # => nil
    
  • \z: Соответствует концу строки:

    /foo\z/.match('bar foo')   # => #<MatchData "foo">
    /foo\z/.match('foo bar')   # => nil
    /foo\z/.match("bar foo\n") # => nil
    
  • \b: Соответствует границе слова, когда не находится внутри скобок; соответствует символу удаления ("0x08") при нахождении внутри скобок:

    /foo\b/.match('foo bar') # => #<MatchData "foo">
    /foo\b/.match('foobar')  # => nil
    
  • \B: Соответствует границе слова:

    /foo\B/.match('foobar')  # => #<MatchData "foo">
    /foo\B/.match('foo bar') # => nil
    
  • \G: Соответствует первой позиции совпадения:

    В методах, таких как String#gsub и String#scan, это меняется при каждой итерации. Изначально оно соответствует началу исходного текста, а при каждой последующей итерации оно соответствует позиции, где завершилось предыдущее совпадение.

    "    a b c".gsub(/ /, '_')   # => "____a_b_c"
    "    a b c".gsub(/\G /, '_') # => "____a b c"
    

    В методах, таких как Regexp#match и String#match, принимающих необязательный параметр смещения, оно соответствует позиции, с которой начинается поиск.

    "hello, world".match(/,/, 3)   # => #<MatchData ",">
    "hello, world".match(/\G,/, 3) # => nil
    

Обрамляющие якоря

Якоря предвосхищения:

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

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

Якоря ретроспективы:

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

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

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

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

Якорь сброса сопоставления

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

    /ab\Kc/.match('abc')    # => #<MatchData "c">
    /(?<=ab)c/.match('abc') # => #<MatchData "c">
    

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

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

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

Альтернация

Символ-метасимвол вертикальной черты (|) может быть использован в скобках для выражения альтернации: два или более подвыражений, любое из которых может соответствовать целевой строке.

Две альтернативы:

re = /(a|b)/
re.match('foo') # => nil
re.match('bar') # => #<MatchData "b" 1:"b">

Четыре альтернативы:

re = /(a|b|c|d)/
re.match('shazam') # => #<MatchData "a" 1:"a">
re.match('cold')   # => #<MatchData "c" 1:"c">

Каждая альтернатива — это подвыражение, и может быть составлена из других подвыражений:

re = /([a-c]|[x-z])/
re.match('bar') # => #<MatchData "b" 1:"b">
re.match('ooz') # => #<MatchData "z" 1:"z">

Метод Regexp.union предоставляет удобный способ построения регулярного выражения с альтернативами.

Квантификаторы

Простое регулярное выражение сопоставляет один символ:

/\w/.match('Hello')  # => #<MatchData "H">

Добавленный квантификатор указывает, сколько совпадений требуется или разрешено:

  • * - Сопоставляется ноль или более раз:

    /\w*/.match('')
    # => #<MatchData "">
    /\w*/.match('x')
    # => #<MatchData "x">
    /\w*/.match('xyz')
    # => #<MatchData "yz">
    
  • + - Сопоставляется один или более раз:

    /\w+/.match('')    # => nil
    /\w+/.match('x')   # => #<MatchData "x">
    /\w+/.match('xyz') # => #<MatchData "xyz">
    
  • ? - Сопоставляется ноль или один раз:

    /\w?/.match('')    # => #<MatchData "">
    /\w?/.match('x')   # => #<MatchData "x">
    /\w?/.match('xyz') # => #<MatchData "x">
    
  • {n} - Сопоставляется ровно n раз:

    /\w{2}/.match('')    # => nil
    /\w{2}/.match('x')   # => nil
    /\w{2}/.match('xyz') # => #<MatchData "xy">
    
  • {min,} - Сопоставляется min или более раз:

    /\w{2,}/.match('')    # => nil
    /\w{2,}/.match('x')   # => nil
    /\w{2,}/.match('xy')  # => #<MatchData "xy">
    /\w{2,}/.match('xyz') # => #<MatchData "xyz">
    
  • {,max} - Сопоставляется max или меньше раз:

    /\w{,2}/.match('')    # => #<MatchData "">
    /\w{,2}/.match('x')   # => #<MatchData "x">
    /\w{,2}/.match('xyz') # => #<MatchData "xy">
    
  • {min,max} - Сопоставляется как минимум min раз и как максимум max раз:

    /\w{1,2}/.match('')    # => nil
    /\w{1,2}/.match('x')   # => #<MatchData "x">
    /\w{1,2}/.match('xyz') # => #<MatchData "xy">
    

Жадный, ленивый или притяжательный поиск

Сопоставление квантификатора может быть жадным, ленивым или притяжательным:

  • При жадном поиске максимально возможное количество вхождений сопоставляется, при условии, что общее совпадение успешно. Жадные квантификаторы: *, +, ?, {min, max} и его варианты.

  • При ленивом поиске минимальное количество вхождений сопоставляется. Ленивые квантификаторы: *?, +?, ??, {min, max}? и его варианты.

  • При притяжательном поиске, после того, как совпадение найдено, откат не происходит; это совпадение сохраняется, даже если оно ставит под угрозу общее совпадение. Притяжательные квантификаторы: *+, ++, ?+. Обратите внимание, что {min, max} и его варианты не поддерживают притяжательное сопоставление.

Подробнее:

  • О жадном и ленивом поиске см. Выбор минимального или максимального повторения.

  • О притяжательном поиске см. Исключить ненужный откат.

Группы и захват

Простое регулярное выражение имеет (как максимум) одно совпадение:

re = /\d\d\d\d-\d\d-\d\d/
re.match('1943-02-04')      # => #<MatchData "1943-02-04">
re.match('1943-02-04').size # => 1
re.match('foo')             # => nil

Добавление одной или нескольких пар скобок, (subexpression), определяет группы, которые могут привести к нескольким совпавшим подстрокам, называемым захватами:

re = /(\d\d\d\d)-(\d\d)-(\d\d)/
re.match('1943-02-04')      # => #<MatchData "1943-02-04" 1:"1943" 2:"02" 3:"04">
re.match('1943-02-04').size # => 4

Первый захват — это вся сопоставленная строка; другие захваты — это совпавшие подстроки из групп.

Группа может иметь квантификатор:

re = /July 4(th)?/
re.match('July 4')   # => #<MatchData "July 4" 1:nil>
re.match('July 4th') # => #<MatchData "July 4th" 1:"th">

re = /(foo)*/
re.match('')       # => #<MatchData "" 1:nil>
re.match('foo')    # => #<MatchData "foo" 1:"foo">
re.match('foofoo') # => #<MatchData "foofoo" 1:"foo">

re = /(foo)+/
re.match('')       # => nil
re.match('foo')    # => #<MatchData "foo" 1:"foo">
re.match('foofoo') # => #<MatchData "foofoo" 1:"foo">

Возвращаемый объект MatchData предоставляет доступ к совпавшим подстрокам:

re = /(\d\d\d\d)-(\d\d)-(\d\d)/
md = re.match('1943-02-04')
# => #<MatchData "1943-02-04" 1:"1943" 2:"02" 3:"04">
md[0] # => "1943-02-04"
md[1] # => "1943"
md[2] # => "02"
md[3] # => "04"

Незахватывающие группы

Группа может быть сделана незахватывающей; она по-прежнему является группой (и, например, может иметь квантификатор), но её совпавшая подстрока не включается среди захватов.

Незахватывающая группа начинается с ?: (внутри скобок):

# Don't capture the year.
re = /(?:\d\d\d\d)-(\d\d)-(\d\d)/
md = re.match('1943-02-04') # => #<MatchData "1943-02-04" 1:"02" 2:"04">

Обратные ссылки

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

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

В этой таблице показано, как каждое подвыражение в регулярном выражении выше сопоставляет подстроку в целевой строке:

| Subexpression in Regexp   | Matching Substring in Target String |
|---------------------------|-------------------------------------|
|       First '[csh]'       |            Character 'c'            |
|          '(..)'           |        First substring 'at'         |
|      First space ' '      |      First space character ' '      |
|       Second '[csh]'      |            Character 's'            |
| '\1' (backreference 'at') |        Second substring 'at'        |
|           ' in'           |            Substring ' in'          |

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

  • Для большого числа групп:

    • Обычная \n нотация применима только для n в диапазоне (1..9).

    • MatchData[n] нотация применима для любого неотрицательного n.

  • \0 — это специальная обратная ссылка, которая ссылается на всю сопоставленную строку; она не может использоваться внутри самого регулярного выражения, но может использоваться вне его (например, в вызове метода подстановки):

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

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

Как видно выше, к захвату можно обратиться по его номеру. Захват также может иметь имя, префикс ?<name> или ?'name', и имя (символическое) можно использовать в качестве индекса в MatchData[]:

md = /\$(?<dollars>\d+)\.(?'cents'\d+)/.match("$3.67")
# => #<MatchData "$3.67" dollars:"3" cents:"67">
md[:dollars]  # => "3"
md[:cents]    # => "67"
# The capture numbers are still valid.
md[2]         # => "67"

Когда регулярное выражение содержит именованные захваты, безымянных захватов нет:

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

Именованная группа может быть обратной ссылкой как \k<name>:

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

Когда (и только когда) регулярное выражение содержит именованные группы захватов и появляется перед оператором =~, сопоставленные подстроки назначаются локальным переменным с соответствующими именами:

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

Метод Regexp#named_captures возвращает хэш имен и подстрок захватов; метод Regexp#names возвращает массив имен захватов.

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

Группа может быть сделана атомной с (?>подвыражение).

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

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

Пример (без атомной группировки):

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

Анализ:

  1. Ведущее подвыражение " в шаблоне соответствует первому символу " в целевой строке.

  2. Следующее подвыражение .* соответствует следующей подстроке Quote“ (включая заключительную двойную кавычку).

  3. Теперь в целевой строке ничего не осталось, чтобы сопоставить заключительное подвыражение " в шаблоне; это привело бы к неудаче общего совпадения.

  4. Сопоставленная подстрока откатывается на одну позицию: Quote.

  5. Заключительное подвыражение " теперь соответствует заключительной подстроке ", и общее совпадение успешно.

Если подвыражение .* сгруппировано атомно, откат отключен, и общее совпадение не происходит:

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

Атомная группировка может влиять на производительность; см. Атомная группа.

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

Как видно выше, номер обратной ссылки (\n) или имя (\k<name>) обеспечивают доступ к захваченному подстроке; соответствующая подвыражение regexp также может быть обработана с помощью номера (\gn) или имени (\g<name>):

/\A(?<paren>\(\g<paren>*\))*\z/.match('(())')
# ^1
#      ^2
#           ^3
#                 ^4
#      ^5
#           ^6
#                      ^7
#                       ^8
#                       ^9
#                           ^10

Шаблон:

  1. Совпадает в начале строки, т. е. перед первым символом.

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

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

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

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

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

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

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

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

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

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

Условные выражения

Условный конструкт имеет вид (?(cond)yes|no), где:

  • cond может быть номером или именем захвата.

  • Применяемое совпадение — yes, если cond захвачен; в противном случае применяется совпадение — no.

  • Если не требуется, |no можно опустить.

Примеры:

re = /\A(foo)?(?(1)(T)|(F))\z/
re.match('fooT') # => #<MatchData "fooT" 1:"foo" 2:"T" 3:nil>
re.match('F')    # => #<MatchData "F" 1:nil 2:nil 3:"F">
re.match('fooF') # => nil
re.match('T')    # => nil

re = /\A(?<xyzzy>foo)?(?(<xyzzy>)(T)|(F))\z/
re.match('fooT') # => #<MatchData "fooT" xyzzy:"foo">
re.match('F')    # => #<MatchData "F" xyzzy:nil>
re.match('fooF') # => nil
re.match('T')    # => nil

Оператор отсутствия

Оператор отсутствия — это специальная группа, которая соответствует любому элементу, который не соответствует содержащимся подвыражениям.

/(?~real)/.match('surrealist') # => #<MatchData "surrea">
/(?~real)ist/.match('surrealist') # => #<MatchData "ealist">
/sur(?~real)ist/.match('surrealist') # => nil

Unicode

Свойства Unicode

Конструкт /\p{property_name}/ (с маленькими p ) соответствует символам с использованием имени свойства Unicode, очень похоже на класс символов; свойство Alpha определяет буквенные символы:

/\p{Alpha}/.match('a') # => #<MatchData "a">
/\p{Alpha}/.match('1') # => nil

Свойство можно инвертировать, добавив перед именем символ каретки (^):

/\p{^Alpha}/.match('1') # => #<MatchData "1">
/\p{^Alpha}/.match('a') # => nil

Или используя \P (заглавные P):

/\P{Alpha}/.match('1') # => #<MatchData "1">
/\P{Alpha}/.match('a') # => nil

См. Свойства Unicode для regexps, основанных на многочисленных свойствах.

Некоторые часто используемые свойства соответствуют выражениям POSIX в квадратных скобках:

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

  • /\p{Alpha}/: Буквенный символ

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

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

  • /\p{Digit}/: Цифровые символы и аналогичные)

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

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

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

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

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

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

Также часто используются:

  • /\p{Emoji}/: Эмодзи Unicode.

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

  • /\p{Word}/: Элемент одной из этих категорий символов Unicode (см. ниже) или имеющий одно из этих свойств Unicode:

    • Категории Unicode:

      • Mark (M).

      • Decimal Number (Nd)

      • Connector Punctuation (Pc).

    • Свойства Unicode:

      • Alpha

      • Join_Control

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

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

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

Категории символов Unicode

Имя категории символа Unicode:

  • Может быть полным именем или сокращенным именем.

  • Регистронезависимо.

  • Рассматривает пробел, дефис и подчеркивание как эквивалентные.

Примеры:

/\p{lu}/                # => /\p{lu}/
/\p{LU}/                # => /\p{LU}/
/\p{Uppercase Letter}/  # => /\p{Uppercase Letter}/
/\p{Uppercase_Letter}/  # => /\p{Uppercase_Letter}/
/\p{UPPERCASE-LETTER}/  # => /\p{UPPERCASE-LETTER}/

Ниже приведены сокращения и имена категорий символов Unicode. Перечисления символов в каждой категории находятся по ссылкам.

Буквы:

  • L, Letter: LC, Lm, или Lo.

  • LC, Cased_Letter: Ll, Lt, или Lu.

  • Lu, Lowercase_Letter.

  • Lu, Modifier_Letter.

  • Lu, Other_Letter.

  • Lu, Titlecase_Letter.

  • Lu, Uppercase_Letter.

Знаки:

  • M, Mark: Mc, Me, или Mn.

  • Mc, Spacing_Mark.

  • Me, Enclosing_Mark.

  • Mn, Nonapacing_Mark.

Цифры:

  • N, Number: Nd, Nl, или No.

  • Nd, Decimal_Number.

  • Nl, Letter_Number.

  • No, Other_Number.

Пунктуация:

  • P, Punctuation: Pc, Pd, Pe, Pf, Pi, Po, или Ps.

  • Pc, Connector_Punctuation.

  • Pd, Dash_Punctuation.

  • Pe, Close_Punctuation.

  • Pf, Final_Punctuation.

  • Pi, Initial_Punctuation.

  • Po, Other_Punctuation.

  • Ps, Open_Punctuation.

  • S, Symbol: Sc, Sk, Sm, или So.

  • Sc, Currency_Symbol.

  • Sk, Modifier_Symbol.

  • Sm, Math_Symbol.

  • So, Other_Symbol.

  • Z, Separator: Zl, Zp, или Zs.

  • Zl, Line_Separator.

  • Zp, Paragraph_Separator.

  • Zs, Space_Separator.

  • C, Other: Cc, Cf, Cn, Co, или Cs.

  • Cc, Control.

  • Cf, Format.

  • Cn, Unassigned.

  • Co, Private_Use.

  • Cs, Surrogate.

Скрипты и блоки Unicode

Среди свойств Unicode:

  • Скрипты Unicode; см. поддерживаемые скрипты.

  • Блоки Unicode; см. поддерживаемые блоки.

Выражения POSIX в квадратных скобках

Выражение POSIX в квадратных скобках также аналогично классу символов. Эти выражения предлагают переносимый альтернативный вариант вышеперечисленного, с добавлением возможности охватить не-ASCII символы:

  • /\d/ соответствует только ASCII десятичным цифрам от 0 до 9.

  • /[[:digit:]]/ соответствует любому символу в категории Unicode Decimal Number (Nd); см. ниже.

Выражения POSIX в квадратных скобках:

END_OF_DOCUMENT_MARKER
  • /[[:digit:]]/: Соответствует числу Юникода:

    /[[:digit:]]/.match('9')       # => #<MatchData "9">
    /[[:digit:]]/.match("\u1fbf9") # => #<MatchData "9">
    
  • /[[:xdigit:]]/: Соответствует цифре, разрешённой в шестнадцатеричном числе; эквивалентно [0-9a-fA-F].

  • /[[:upper:]]/: Соответствует заглавной букве Юникода:

    /[[:upper:]]/.match('A')      # => #<MatchData "A">
    /[[:upper:]]/.match("\u00c6") # => #<MatchData "Æ">
    
  • /[[:lower:]]/: Соответствует строчной букве Юникода:

    /[[:lower:]]/.match('a')      # => #<MatchData "a">
    /[[:lower:]]/.match("\u01fd") # => #<MatchData "ǽ">
    
  • /[[:alpha:]]/: Соответствует /[[:upper:]]/ или /[[:lower:]]/.

  • /[[:alnum:]]/: Соответствует /[[:alpha:]]/ или /[[:digit:]]/.

  • /[[:space:]]/: Соответствует пробельному символу Юникода:

    /[[:space:]]/.match(' ')      # => #<MatchData " ">
    /[[:space:]]/.match("\u2005") # => #<MatchData " ">
    
  • /[[:blank:]]/: Соответствует /[[:space:]]/ или табуляции:

    /[[:blank:]]/.match(' ')      # => #<MatchData " ">
    /[[:blank:]]/.match("\u2005") # => #<MatchData " ">
    /[[:blank:]]/.match("\t")     # => #<MatchData "\t">
    
  • /[[:cntrl:]]/: Соответствует управляющему символу Юникода:

    /[[:cntrl:]]/.match("\u0000") # => #<MatchData "\u0000">
    /[[:cntrl:]]/.match("\u009f") # => #<MatchData "\u009F">
    
  • /[[:graph:]]/: Соответствует любому символу, кроме /[[:space:]]/ или /[[:cntrl:]]/.

  • /[[:print:]]/: Соответствует /[[:graph:]]/ или пробельному символу.

  • /[[:punct:]]/: Соответствует любому символу пунктуации Юникода [www.compart.com/en/unicode/category/Po]:

Ruby также поддерживает эти (не POSIX) выражения в квадратных скобках:

  • /[[:ascii:]]/: Соответствует символу в наборе символов ASCII.

  • /[[:word:]]/: Соответствует символу в одной из этих категорий символов Юникода или имеющему одно из этих свойств Юникода:

    • Категории Юникода:

      • Mark (M).

      • Decimal Number (Nd)

      • Connector Punctuation (Pc).

    • Свойства Юникода:

      • Alpha

      • Join_Control

Комментарии

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

/foo(?#Ignore me)bar/.match('foobar') # => #<MatchData "foobar">

Комментарий не может включать неэкранированный символ-разделитель.

См. также Расширенный режим.

Режимы

Каждый из этих модификаторов устанавливает режим для regexp:

  • i: /pattern/i устанавливает Регистронезависимый режим.

  • m: /pattern/m устанавливает Многострочный режим.

  • x: /pattern/x устанавливает Расширенный режим.

  • o: /pattern/o устанавливает Режим интерполяции.

Можно применить любые, все или ни одного из них.

Модификаторы i, m, и x могут быть применены к подвыражениям:

  • (?modifier) включает режим «вкл» для последующих подвыражений

  • (?-modifier) включает режим «выкл» для последующих подвыражений

  • (?modifier:subexp) включает режим «вкл» для subexp в группе

  • (?-modifier:subexp) включает режим «выкл» для subexp в группе

Пример:

re = /(?i)te(?-i)st/
re.match('test') # => #<MatchData "test">
re.match('TEst') # => #<MatchData "TEst">
re.match('TEST') # => nil
re.match('teST') # => nil

re = /t(?i:e)st/
re.match('test') # => #<MatchData "test">
re.match('tEst') # => #<MatchData "tEst">
re.match('tEST') # => nil

Метод Regexp#options возвращает целое число, значение которого показывает настройки для регистронезависимого режима, многострочного режима и расширенного режима.

Регистронезависимый режим

По умолчанию, regexp чувствителен к регистру:

/foo/.match('FOO')  # => nil

Модификатор i включает регистронезависимый режим:

/foo/i.match('FOO')
# => #<MatchData "FOO">

Метод Regexp#casefold? возвращает, является ли режим регистронезависимым.

Многострочный режим

Многострочный режим в Ruby — это то, что обычно называют режимом «точка-всё»:

  • Без модификатора m, подвыражение . не соответствует переходам на новую строку:

    /a.c/.match("a\nc")  # => nil
    
  • С модификатором, оно соответствует:

    /a.c/m.match("a\nc") # => #<MatchData "a\nc">
    

В отличие от других языков, модификатор m не влияет на якоря ^ и $. Эти якоря всегда соответствуют границам строк в Ruby.

Расширенный режим

Модификатор x включает расширенный режим, что означает:

  • Пробелы в шаблоне игнорируются.

  • Символ # помечает остаток строки как комментарий, который также игнорируется для сопоставления.

В расширенном режиме пробелы и комментарии могут использоваться для создания самодокументированного regexp.

Regexp без расширенного режима (соответствует некоторым римским цифрам):

pattern = '^M{0,3}(CM|CD|D?C{0,3})(XC|XL|L?X{0,3})(IX|IV|V?I{0,3})$'
re = /#{pattern}/
re.match('MCMXLIII') # => #<MatchData "MCMXLIII" 1:"CM" 2:"XL" 3:"III">

Regexp в расширенном режиме:

pattern = <<-EOT
  ^                   # beginning of string
  M{0,3}              # thousands - 0 to 3 Ms
  (CM|CD|D?C{0,3})    # hundreds - 900 (CM), 400 (CD), 0-300 (0 to 3 Cs),
                      #            or 500-800 (D, followed by 0 to 3 Cs)
  (XC|XL|L?X{0,3})    # tens - 90 (XC), 40 (XL), 0-30 (0 to 3 Xs),
                      #        or 50-80 (L, followed by 0 to 3 Xs)
  (IX|IV|V?I{0,3})    # ones - 9 (IX), 4 (IV), 0-3 (0 to 3 Is),
                      #        or 5-8 (V, followed by 0 to 3 Is)
  $                   # end of string
EOT
re = /#{pattern}/x
re.match('MCMXLIII') # => #<MatchData "MCMXLIII" 1:"CM" 2:"XL" 3:"III">

Режим интерполяции

Модификатор o означает, что в первый раз, когда встречается литеральный regexp с интерполяциями, сгенерированный объект Regexp сохраняется и используется для всех последующих вычислений этого литерального regexp. Без модификатора o, сгенерированный объект Regexp не сохраняется, поэтому каждое вычисление литерального regexp генерирует новый объект Regexp.

Без модификатора o:

def letters; sleep 5; /[A-Z][a-z]/; end
words = %w[abc def xyz]
start = Time.now
words.each {|word| word.match(/\A[#{letters}]+\z/) }
Time.now - start # => 15.0174892

С модификатором o:

start = Time.now
words.each {|word| word.match(/\A[#{letters}]+\z/o) }
Time.now - start # => 5.0010866

Обратите внимание, что если литеральный regexp не содержит интерполяций, поведение o является стандартным.

Кодировки

По умолчанию, regexp только с символами US-ASCII имеет кодировку US-ASCII:

re = /foo/
re.source.encoding # => #<Encoding:US-ASCII>
re.encoding        # => #<Encoding:US-ASCII>

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

  • /pat/n: US-ASCII, если содержит только символы US-ASCII, в противном случае ASCII-8BIT:

    /foo/n.encoding     # => #<Encoding:US-ASCII>
    /foo\xff/n.encoding # => #<Encoding:ASCII-8BIT>
    /foo\x7f/n.encoding # => #<Encoding:US-ASCII>
    
  • /pat/u: UTF-8

    /foo/u.encoding # => #<Encoding:UTF-8>
    
  • /pat/e: EUC-JP

    /foo/e.encoding # => #<Encoding:EUC-JP>
    
  • /pat/s: Windows-31J

    /foo/s.encoding # => #<Encoding:Windows-31J>
    

Regexp может быть сопоставлен с целевой строкой, если:

  • У них одинаковая кодировка.

  • Кодировка regexp — это фиксированная кодировка, а строка содержит только символы ASCII. Method Regexp#fixed_encoding? возвращает, имеет ли regexp фиксированную кодировку.

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

Пример:

re = eval("# encoding: ISO-8859-1\n/foo\\xff?/")
re.encoding                 # => #<Encoding:ISO-8859-1>
re =~ "foo".encode("UTF-8") # => 0
re =~ "foo\u0100"           # Raises Encoding::CompatibilityError

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

# Regexp with encoding ISO-8859-1.
re = Regexp.new("a".force_encoding('iso-8859-1'), Regexp::FIXEDENCODING)
re.encoding  # => #<Encoding:ISO-8859-1>
# Target string with encoding UTF-8.
s = "a\u3042"
s.encoding   # => #<Encoding:UTF-8>
re.match(s)  # Raises Encoding::CompatibilityError.

Таймауты

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

Regexp имеет два значения таймаута:

  • По умолчанию таймаут класса, используемый для regexp, для которого таймаут экземпляра — nil; этот таймаут изначально равен nil, и может быть установлен методом Regexp.timeout=:

    Regexp.timeout # => nil
    Regexp.timeout = 3.0
    Regexp.timeout # => 3.0
    
  • Таймаут экземпляра, который по умолчанию равен nil и может быть установлен в Regexp.new:

    re = Regexp.new('foo', timeout: 5.0)
    re.timeout # => 5.0
    

Когда regexp.timeout — nil, таймаут «проходит» к Regexp.timeout; когда regexp.timeout — не-nil, это значение контролирует таймаут:

| regexp.timeout Value | Regexp.timeout Value |            Result           |
|----------------------|----------------------|-----------------------------|
|         nil          |          nil         |       Never times out.      |
|         nil          |         Float        | Times out in Float seconds. |
|        Float         |          Any         | Times out in Float seconds. |

Оптимизация

Для определённых значений шаблона и целевой строки время сопоставления может увеличиваться полиномиально или экспоненциально по отношению к размеру входных данных; потенциальная уязвимость, возникающая из-за этого, — это атака на отказ в обслуживании с помощью регулярного выражения (ReDoS).

Сопоставление regexp может применять оптимизацию для предотвращения атак ReDoS. При применении оптимизации время сопоставления увеличивается линейно (не полиномиально или экспоненциально) по отношению к размеру входных данных, и атака ReDoS невозможна.

Эта оптимизация применяется, если шаблон соответствует этим критериям:

  • Без обратных ссылок.

  • Без вызовов подвыражений.

  • Без вложенных якорей вокруг или атомарных групп.

  • Без вложенных квантификаторов с подсчётом (т.е. без вложенных {n}, {min,}, {,max}, или {min,max} стилей квантификаторов)

Можно использовать метод Regexp.linear_time?, чтобы определить, соответствует ли шаблон этим критериям:

Regexp.linear_time?(/a*/)     # => true
Regexp.linear_time?('a*')     # => true
Regexp.linear_time?(/(a*)\1/) # => false

Однако ненадежный источник может быть небезопасным, даже если метод возвращает true, потому что оптимизация использует запоминание (что может привести к большому потреблению памяти).

Ссылки

Читать (онлайн PDF книги):

  • Mastering Regular Expressions Джеффри Э.Ф. Фридла.

  • Regular Expressions Cookbook Яна Гойвертса и Стивена Левитана.

Изучать, тестировать (интерактивный онлайн-редактор):

  • Rubular.

Константы

EXTENDED

см. Regexp.options и Regexp.new

FIXEDENCODING

см. Regexp.options и Regexp.new

IGNORECASE

см. Regexp.options и Regexp.new

MULTILINE

см. Regexp.options и Regexp.new

NOENCODING

см. Regexp.options и Regexp.new

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

compile(*args)

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

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

Возвращает новую строку, в которой эскейпены все символы, имеющие специальное значение в регулярном выражении:

s = Regexp.escape('\*?{}.')      # => "\\\\\\*\\?\\{\\}\\."

Для любой строки s, этот вызов возвращает объект MatchData:

r = Regexp.new(Regexp.escape(s)) # => /\\\\\\\*\\\?\\\{\\\}\\\./
r.match(s)                       # => #<MatchData "\\\\\\*\\?\\{\\}\\.">
json_create(object) Показать исходный код
# File ext/json/lib/json/add/regexp.rb, line 9
def self.json_create(object)
  new(object['s'], object['o'])
end

См. as_json.

last_match → matchdata or nil Показать исходный код
last_match(n) → string or nil
last_match(name) → string or nil
static VALUE
rb_reg_s_last_match(int argc, VALUE *argv, VALUE _)
{
    if (rb_check_arity(argc, 0, 1) == 1) {
        VALUE match = rb_backref_get();
        int n;
        if (NIL_P(match)) return Qnil;
        n = match_backref_number(match, argv[0]);
        return rb_reg_nth_match(n, match);
    }
    return match_getter();
}

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

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

С положительным целым аргументом n, возвращает _n_-й элемент в matchdata, если он существует, или nil, если нет:

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

С отрицательным целым аргументом n, считает назад от последнего элемента:

Regexp.last_match(-1)       # => "a"

С строковым или символьным аргументом name, возвращает строковое значение для именованного захвата, если оно существует:

/(?<lhs>\w+)\s*=\s*(?<rhs>\w+)/ =~ 'var = val'
Regexp.last_match        # => #<MatchData "var = val" lhs:"var"rhs:"val">
Regexp.last_match(:lhs)  # => "var"
Regexp.last_match('rhs') # => "val"
Regexp.last_match('foo') # Raises IndexError.
linear_time?(re) Показать исходный код
linear_time?(string, options = 0)
static VALUE
rb_reg_s_linear_time_p(int argc, VALUE *argv, VALUE self)
{
    struct reg_init_args args;
    VALUE re = reg_extract_args(argc, argv, &args);

    if (NIL_P(re)) {
        re = reg_init_args(rb_reg_alloc(), args.str, args.enc, args.flags);
    }

    return RBOOL(onig_check_linear_time(RREGEXP_PTR(re)));
}

Возвращает true если сопоставление с re может быть выполнено за линейное время относительно входной строки.

Regexp.linear_time?(/re/) # => true

Обратите внимание, что это свойство интерпретатора Ruby, а не аргумента регулярного выражения. Идентичные регулярные выражения могут или не могут выполняться за линейное время в зависимости от вашей бинарной версии Ruby. Гарантии обратной совместимости относительно значения возвращаемого этим методом нет. Наш текущий алгоритм — (*1), но это может измениться в будущем. Альтернативные реализации также могут вести себя по-разному. Они могут всегда возвращать false для всего.

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

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

    if (NIL_P(re)) {
        reg_init_args(self, args.str, args.enc, args.flags);
    }
    else {
        reg_copy(self, re);
    }

    set_timeout(&RREGEXP_PTR(self)->timelimit, args.timeout);

    return self;
}

При указанном аргументе string, возвращает новый regexp со строкой и параметрами:

r = Regexp.new('foo') # => /foo/
r.source              # => "foo"
r.options             # => 0

Необязательный аргумент options является одним из следующих:

  • Строка параметров:

    Regexp.new('foo', 'i')  # => /foo/i
    Regexp.new('foo', 'im') # => /foo/im
    
  • Побитовое ИЛИ одного или нескольких констант Regexp::EXTENDED, Regexp::IGNORECASE, Regexp::MULTILINE и Regexp::NOENCODING:

    Regexp.new('foo', Regexp::IGNORECASE) # => /foo/i
    Regexp.new('foo', Regexp::EXTENDED)   # => /foo/x
    Regexp.new('foo', Regexp::MULTILINE)  # => /foo/m
    Regexp.new('foo', Regexp::NOENCODING)  # => /foo/n
    flags = Regexp::IGNORECASE | Regexp::EXTENDED |  Regexp::MULTILINE
    Regexp.new('foo', flags)              # => /foo/mix
    
  • nil или false, которые игнорируются.

  • Любое другое ненулевое значение, в таком случае regexp будет регистронезависимым.

Если указан необязательный ключевой аргумент timeout, его числовое значение переопределяет интервал таймаута для класса Regexp.timeout. Если nil передаётся как +timeout, используется интервал таймаута для класса Regexp.timeout.

С указанным аргументом regexp, возвращает новый regexp. Источник, параметры, таймаут такие же, как у regexp. Аргументы options и n_flag неэффективны. Таймаут можно переопределить с помощью ключевого аргумента timeout.

options = Regexp::MULTILINE
r = Regexp.new('foo', options, timeout: 1.1) # => /foo/m
r2 = Regexp.new(r)                           # => /foo/m
r2.timeout                                   # => 1.1
r3 = Regexp.new(r, timeout: 3.14)            # => /foo/m
r3.timeout                                   # => 3.14
escape(string) → new_string Показать исходный код
static VALUE
rb_reg_s_quote(VALUE c, VALUE str)
{
    return rb_reg_quote(reg_operand(str, TRUE));
}

Возвращает новую строку, в которой эскейпены все символы, имеющие специальное значение в регулярном выражении:

s = Regexp.escape('\*?{}.')      # => "\\\\\\*\\?\\{\\}\\."

Для любой строки s, этот вызов возвращает объект MatchData:

r = Regexp.new(Regexp.escape(s)) # => /\\\\\\\*\\\?\\\{\\\}\\\./
r.match(s)                       # => #<MatchData "\\\\\\*\\?\\{\\}\\.">
timeout → float or nil Показать исходный код
static VALUE
rb_reg_s_timeout_get(VALUE dummy)
{
    double d = hrtime2double(rb_reg_match_time_limit);
    if (d == 0.0) return Qnil;
    return DBL2NUM(d);
}

Возвращает текущий интервал таймаута по умолчанию для сопоставления Regexp в секундах. nil означает отсутствие конфигурации таймаута по умолчанию.

timeout = float or nil Показать исходный код
static VALUE
rb_reg_s_timeout_set(VALUE dummy, VALUE timeout)
{
    rb_ractor_ensure_main_ractor("can not access Regexp.timeout from non-main Ractors");

    set_timeout(&rb_reg_match_time_limit, timeout);

    return timeout;
}

Устанавливает интервал таймаута по умолчанию для сопоставления Regexp в секундах. nil означает отсутствие конфигурации таймаута по умолчанию. Эта конфигурация глобальна для процесса. Если вы хотите установить таймаут для каждого Regexp, используйте ключевой аргумент timeout для Regexp.new.

Regexp.timeout = 1
/^a*b?a*$/ =~ "a" * 100000 + "x" #=> regexp match timeout (RuntimeError)
try_convert(object) → regexp or nil Показать исходный код
static VALUE
rb_reg_s_try_convert(VALUE dummy, VALUE re)
{
    return rb_check_regexp_type(re);
}

Возвращает object если это regexp:

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

В противном случае, если object отвечает на :to_regexp, вызывает object.to_regexp и возвращает результат.

Возвращает nil если object не отвечает на :to_regexp.

Regexp.try_convert('re') # => nil

Вызывает исключение, если object.to_regexp не возвращает regexp.

union(*patterns) → regexp Показать исходный код
union(array_of_patterns) → regexp
static VALUE
rb_reg_s_union_m(VALUE self, VALUE args)
{
    VALUE v;
    if (RARRAY_LEN(args) == 1 &&
        !NIL_P(v = rb_check_array_type(rb_ary_entry(args, 0)))) {
        return rb_reg_s_union(self, v);
    }
    return rb_reg_s_union(self, args);
}

Возвращает новый regexp, являющийся объединением заданных шаблонов:

r = Regexp.union(%w[cat dog])      # => /cat|dog/
r.match('cat')      # => #<MatchData "cat">
r.match('dog')      # => #<MatchData "dog">
r.match('cog')      # => nil

Для каждого шаблона, являющегося строкой, используется Regexp.new(pattern).

Regexp.union('penzance')             # => /penzance/
Regexp.union('a+b*c')                # => /a\+b\*c/
Regexp.union('skiing', 'sledding')   # => /skiing|sledding/
Regexp.union(['skiing', 'sledding']) # => /skiing|sledding/

Для каждого шаблона, являющегося regexp, он используется как есть, включая его флаги:

Regexp.union(/foo/i, /bar/m, /baz/x)
# => /(?i-mx:foo)|(?m-ix:bar)|(?x-mi:baz)/
Regexp.union([/foo/i, /bar/m, /baz/x])
# => /(?i-mx:foo)|(?m-ix:bar)|(?x-mi:baz)/

Без аргументов возвращает /(?!)/:

Regexp.union # => /(?!)/

Если какой-либо шаблон regexp содержит захваты, поведение не определено.

END_OF_DOCUMENT_MARKER

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

regexp == object → true или false

Возвращает true если object является другим объектом Regexp, шаблон, флаги и кодировка которого совпадают с self, false в противном случае:

/foo/ == Regexp.new('foo')                          # => true
/foo/ == /foo/i                                     # => false
/foo/ == Regexp.new('food')                         # => false
/foo/ == Regexp.new("abc".force_encoding("euc-jp")) # => false
Псевдоним для: eql?
regexp === string → true или false Показать исходный код
static VALUE
rb_reg_eqq(VALUE re, VALUE str)
{
    long start;

    str = reg_operand(str, FALSE);
    if (NIL_P(str)) {
        rb_backref_set(Qnil);
        return Qfalse;
    }
    start = rb_reg_search(re, str, 0, 0);
    return RBOOL(start >= 0);
}

Возвращает true если self находит совпадение в string:

/^[a-z]*$/ === 'HELLO' # => false
/^[A-Z]*$/ === 'HELLO' # => true

Этот метод используется в операторах case:

s = 'HELLO'
case s
when /\A[a-z]*\z/; print "Lower case\n"
when /\A[A-Z]*\z/; print "Upper case\n"
else               print "Mixed case\n"
end # => "Upper case"
regexp =~ string → целое число или nil Показать исходный код
VALUE
rb_reg_match(VALUE re, VALUE str)
{
    long pos = reg_match_pos(re, &str, 0, NULL);
    if (pos < 0) return Qnil;
    pos = rb_str_sublen(str, pos);
    return LONG2FIX(pos);
}

Возвращает целое число индекса (в символах) первого совпадения для self и string, или nil если совпадение не найдено; также устанавливает глобальные переменные Regexp:

/at/ =~ 'input data' # => 7
$~                   # => #<MatchData "at">
/ax/ =~ 'input data' # => nil
$~                   # => nil

Присваивает именованные capture локальным переменным с такими же именами, если и только если self:

  • Это regexp-литерал; см. Regexp-литералы.

  • Не содержит интерполяции; см. интерполяцию Regexp.

  • Находится слева от выражения.

Пример:

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

Присваивает nil если совпадения не найдено:

/(?<lhs>\w+)\s*=\s*(?<rhs>\w+)/ =~ '  x = '
p lhs # => nil
p rhs # => nil

Не выполняет присваивание локальных переменных, если self не является regexp-литералом:

r = /(?<foo>\w+)\s*=\s*(?<foo>\w+)/
r =~ '  x = y  '
p foo # Undefined local variable
p bar # Undefined local variable

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

'  x = y  ' =~ /(?<foo>\w+)\s*=\s*(?<foo>\w+)/
p foo, foo # Undefined local variables

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

r = /(?<foo>\w+)/
/(?<foo>\w+)\s*=\s*#{r}/ =~ 'x = y'
p foo # Undefined local variable
as_json(*) Показать исходный код
# File ext/json/lib/json/add/regexp.rb, line 28
def as_json(*)
  {
    JSON.create_id => self.class.name,
    'o'            => options,
    's'            => source,
  }
end

Методы Regexp#as_json и Regexp.json_create могут использоваться для сериализации и десериализации объекта Regexp; см. Marshal.

Метод Regexp#as_json сериализует self, возвращая хеш из 2 элементов, представляющий self:

require 'json/add/regexp'
x = /foo/.as_json
# => {"json_class"=>"Regexp", "o"=>0, "s"=>"foo"}

Метод JSON.create десериализует такой хеш, возвращая объект Regexp:

Regexp.json_create(x) # => /foo/
casefold?→ true или false Показать исходный код
static VALUE
rb_reg_casefold_p(VALUE re)
{
    rb_reg_check(re);
    return RBOOL(RREGEXP_PTR(re)->options & ONIG_OPTION_IGNORECASE);
}

Возвращает true если флаг игнорирования регистра в self установлен, false в противном случае:

/a/.casefold?           # => false
/a/i.casefold?          # => true
/(?i:a)/.casefold?      # => false
encoding → кодировка Показать исходный код
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.

eql?
Также алиас для: ==
fixed_encoding? → true или false Показать исходный код
static VALUE
rb_reg_fixed_encoding_p(VALUE re)
{
    return RBOOL(FL_TEST(re, KCODE_FIXED));
}

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

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

r = /a/u                                        # => /a/
r.fixed_encoding?                               # => true
r.match?("\u{6666} a")                          # => true
r.match?("\xa1\xa2".force_encoding("euc-jp"))   # Raises exception.
r.match?("abc".force_encoding("euc-jp"))        # => true

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

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

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

inspect → строка Показать исходный код
static VALUE
rb_reg_inspect(VALUE re)
{
    if (!RREGEXP_PTR(re) || !RREGEXP_SRC(re) || !RREGEXP_SRC_PTR(re)) {
        return rb_any_to_s(re);
    }
    return rb_reg_desc(re);
}

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

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

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

match(string, offset = 0) → matchdata или nil Показать исходный код
match(string, offset = 0) {|matchdata| ... } → object
static VALUE
rb_reg_match_m(int argc, VALUE *argv, VALUE re)
{
    VALUE result = Qnil, str, initpos;
    long pos;

    if (rb_scan_args(argc, argv, "11", &str, &initpos) == 2) {
        pos = NUM2LONG(initpos);
    }
    else {
        pos = 0;
    }

    pos = reg_match_pos(re, &str, pos, &result);
    if (pos < 0) {
        rb_backref_set(Qnil);
        return Qnil;
    }
    rb_match_busy(result);
    if (!NIL_P(result) && rb_block_given_p()) {
        return rb_yield(result);
    }
    return result;
}

Без блока возвращает объект MatchData, описывающий совпадение, если оно есть, или nil если совпадения нет; поиск начинается с указанного символа offset в string:

/abra/.match('abracadabra')      # => #<MatchData "abra">
/abra/.match('abracadabra', 4)   # => #<MatchData "abra">
/abra/.match('abracadabra', 8)   # => nil
/abra/.match('abracadabra', 800) # => nil

string = "\u{5d0 5d1 5e8 5d0}cadabra"
/abra/.match(string, 7)          #=> #<MatchData "abra">
/abra/.match(string, 8)          #=> nil
/abra/.match(string.b, 8)        #=> #<MatchData "abra">

С блоком вызывает блок только в случае совпадения; возвращает значение блока:

/abra/.match('abracadabra') {|matchdata| p matchdata }
# => #<MatchData "abra">
/abra/.match('abracadabra', 4) {|matchdata| p matchdata }
# => #<MatchData "abra">
/abra/.match('abracadabra', 8) {|matchdata| p matchdata }
# => nil
/abra/.match('abracadabra', 8) {|marchdata| fail 'Cannot happen' }
# => nil

Вывод (из первых двух блоков выше):

#<MatchData "abra">
#<MatchData "abra">

 /(.)(.)(.)/.match("abc")[2] # => "b"
 /(.)(.)/.match("abc", 1)[2] # => "c"
match?(string) → true или false Показать исходный код
match?(string, offset = 0) → true или false
static VALUE
rb_reg_match_m_p(int argc, VALUE *argv, VALUE re)
{
    long pos = rb_check_arity(argc, 1, 2) > 1 ? NUM2LONG(argv[1]) : 0;
    return rb_reg_match_p(re, argv[0], pos);
}

Возвращает true или false для указания совпадения regexp или его отсутствия без обновления $~ и других связанных переменных. Если второй параметр присутствует, он указывает позицию в строке для начала поиска.

/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;
}

Возвращает хеш, представляющий именованные capture self (см. Именованные capture):

  • Каждый ключ — имя именованного capture.

  • Каждое значение — массив целых индексов для этого именованного capture.

Примеры:

/(?<foo>.)(?<bar>.)/.named_captures # => {"foo"=>[1], "bar"=>[2]}
/(?<foo>.)(?<foo>.)/.named_captures # => {"foo"=>[1, 2]}
/(.)(.)/.named_captures             # => {}
names → массив_имен Показать исходный код
static VALUE
rb_reg_names(VALUE re)
{
    VALUE ary;
    rb_reg_check(re);
    ary = rb_ary_new_capa(onig_number_of_names(RREGEXP_PTR(re)));
    onig_foreach_name(RREGEXP_PTR(re), reg_names_iter, (void*)ary);
    return ary;
}

Возвращает массив имён capture (см. Именованные capture):

/(?<foo>.)(?<bar>.)(?<baz>.)/.names # => ["foo", "bar", "baz"]
/(?<foo>.)(?<foo>.)/.names          # => ["foo"]
/(.)(.)/.names                      # => []
options → целое число Показать исходный код
static VALUE
rb_reg_options_m(VALUE re)
{
    int options = rb_reg_options(re);
    return INT2NUM(options);
}

Возвращает целое число, биты которого показывают установленные опции в self.

Биты опций:

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

Примеры:

/foo/.options    # => 0
/foo/i.options   # => 1
/foo/x.options   # => 2
/foo/m.options   # => 4
/foo/mix.options # => 7

Обратите внимание, что в возвращаемом целом числе могут быть установлены дополнительные биты; они сохраняются внутри self, игнорируются, если передаются в Regexp.new, и могут быть проигнорированы вызывающей стороной:

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

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

    rb_reg_check(re);
    str = rb_str_dup(RREGEXP_SRC(re));
    return str;
}

Возвращает исходную строку self:

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

Regexp escape-последовательности сохраняются:

/\x20\+/.source  # => "\\x20\\+"

Символы экранирования лексического анализатора не сохраняются:

/\//.source  # => "/"
END_OF_DOCUMENT_MARKER
timeout → число с плавающей точкой или nil Показать исходный код
static VALUE
rb_reg_timeout_get(VALUE re)
{
    rb_reg_check(re);
    double d = hrtime2double(RREGEXP_PTR(re)->timelimit);
    if (d == 0.0) return Qnil;
    return DBL2NUM(d);
}

Возвращает интервал таймаута для сопоставления Regexp в секундах. nil означает отсутствие конфигурации таймаута по умолчанию.

Эта конфигурация задается для каждого объекта. Глобальная конфигурация, установленная с помощью Regexp.timeout=, игнорируется, если настроена конфигурация для каждого объекта.

re = Regexp.new("^a*b?a*$", timeout: 1)
re.timeout               #=> 1.0
re =~ "a" * 100000 + "x" #=> regexp match timeout (RuntimeError)
to_json(*args) Показать исходный код
# File ext/json/lib/json/add/regexp.rb, line 45
def to_json(*args)
  as_json.to_json(*args)
end

Возвращает строку JSON, представляющую self:

require 'json/add/regexp'
puts /foo/.to_json

Вывод:

{"json_class":"Regexp","o":0,"s":"foo"}
to_s → строка Показать исходный код
static VALUE
rb_reg_to_s(VALUE re)
{
    return rb_reg_str_with_term(re, '/');
}

Возвращает строку, отображающую опции и строку self:

r0 = /ab+c/ix
s0 = r0.to_s # => "(?ix-m:ab+c)"

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

r1 = Regexp.new(s0) # => /(?ix-m:ab+c)/
r2 = /#{s0}/        # => /(?ix-m:ab+c)/

Обратите внимание, что r1 и r2 не равны r0 из-за различий в исходных строках:

r0 == r1  # => false
r0.source # => "ab+c"
r1.source # => "(?ix-m:ab+c)"

Связанное: Regexp#inspect.

~ rxp → целое число или nil Показать исходный код
VALUE
rb_reg_match2(VALUE re)
{
    long start;
    VALUE line = rb_lastline_get();

    if (!RB_TYPE_P(line, T_STRING)) {
        rb_backref_set(Qnil);
        return Qnil;
    }

    start = rb_reg_search(re, line, 0, 0);
    if (start < 0) {
        return Qnil;
    }
    start = rb_str_sublen(line, start);
    return LONG2FIX(start);
}

Эквивалентно rxp =~ $_:

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

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

Spec-Zone.ru

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