2009-09-17 21:11:55 -04:00
|
|
|
# -*- mode: rdoc; coding: utf-8; fill-column: 74; -*-
|
|
|
|
|
|
|
|
Regular expressions (<i>regexp</i>s) are patterns which describe the
|
|
|
|
contents of a string. They're used for testing whether a string contains a
|
|
|
|
given pattern, or extracting the portions that match. They are created
|
|
|
|
with the <tt>/</tt><i>pat</i><tt>/</tt> and
|
|
|
|
<tt>%r{</tt><i>pat</i><tt>}</tt> literals or the <tt>Regexp.new</tt>
|
|
|
|
constructor.
|
|
|
|
|
|
|
|
A regexp is usually delimited with forward slashes (<tt>/</tt>). For
|
|
|
|
example:
|
|
|
|
|
|
|
|
/hay/ =~ 'haystack' #=> 0
|
|
|
|
/y/.match('haystack') #=> #<MatchData "y">
|
|
|
|
|
|
|
|
If a string contains the pattern it is said to <i>match</i>. A literal
|
|
|
|
string matches itself.
|
|
|
|
|
2013-09-16 23:56:32 -04:00
|
|
|
Here 'haystack' does not contain the pattern 'needle', so it doesn't match:
|
|
|
|
|
2009-09-17 21:11:55 -04:00
|
|
|
/needle/.match('haystack') #=> nil
|
2013-09-16 23:56:32 -04:00
|
|
|
|
|
|
|
Here 'haystack' contains the pattern 'hay', so it matches:
|
|
|
|
|
2009-09-17 21:11:55 -04:00
|
|
|
/hay/.match('haystack') #=> #<MatchData "hay">
|
|
|
|
|
|
|
|
Specifically, <tt>/st/</tt> requires that the string contains the letter
|
|
|
|
_s_ followed by the letter _t_, so it matches _haystack_, also.
|
|
|
|
|
2011-12-07 18:22:30 -05:00
|
|
|
== <tt>=~</tt> and Regexp#match
|
|
|
|
|
|
|
|
Pattern matching may be achieved by using <tt>=~</tt> operator or Regexp#match
|
|
|
|
method.
|
|
|
|
|
|
|
|
=== <tt>=~</tt> operator
|
|
|
|
|
|
|
|
<tt>=~</tt> is Ruby's basic pattern-matching operator. When one operand is a
|
2012-06-08 19:17:00 -04:00
|
|
|
regular expression and the other is a string then the regular expression is
|
|
|
|
used as a pattern to match against the string. (This operator is equivalently
|
|
|
|
defined by Regexp and String so the order of String and Regexp do not matter.
|
|
|
|
Other classes may have different implementations of <tt>=~</tt>.) If a match
|
|
|
|
is found, the operator returns index of first match in string, otherwise it
|
|
|
|
returns +nil+.
|
2011-12-07 18:22:30 -05:00
|
|
|
|
|
|
|
/hay/ =~ 'haystack' #=> 0
|
2012-06-08 19:17:00 -04:00
|
|
|
'haystack' =~ /hay/ #=> 0
|
2011-12-07 18:22:30 -05:00
|
|
|
/a/ =~ 'haystack' #=> 1
|
|
|
|
/u/ =~ 'haystack' #=> nil
|
|
|
|
|
|
|
|
Using <tt>=~</tt> operator with a String and Regexp the <tt>$~</tt> global
|
|
|
|
variable is set after a successful match. <tt>$~</tt> holds a MatchData
|
|
|
|
object. Regexp.last_match is equivalent to <tt>$~</tt>.
|
|
|
|
|
|
|
|
=== Regexp#match method
|
|
|
|
|
2013-09-16 23:56:32 -04:00
|
|
|
The #match method returns a MatchData object:
|
2011-12-07 18:22:30 -05:00
|
|
|
|
|
|
|
/st/.match('haystack') #=> #<MatchData "st">
|
|
|
|
|
2009-09-17 21:11:55 -04:00
|
|
|
== Metacharacters and Escapes
|
|
|
|
|
|
|
|
The following are <i>metacharacters</i> <tt>(</tt>, <tt>)</tt>,
|
|
|
|
<tt>[</tt>, <tt>]</tt>, <tt>{</tt>, <tt>}</tt>, <tt>.</tt>, <tt>?</tt>,
|
|
|
|
<tt>+</tt>, <tt>*</tt>. They have a specific meaning when appearing in a
|
|
|
|
pattern. To match them literally they must be backslash-escaped. To match
|
2017-10-21 19:38:17 -04:00
|
|
|
a backslash literally, backslash-escape it: <tt>\\\\</tt>.
|
2009-09-17 21:11:55 -04:00
|
|
|
|
|
|
|
/1 \+ 2 = 3\?/.match('Does 1 + 2 = 3?') #=> #<MatchData "1 + 2 = 3?">
|
2017-10-21 19:38:17 -04:00
|
|
|
/a\\\\b/.match('a\\\\b') #=> #<MatchData "a\\b">
|
2009-09-17 21:11:55 -04:00
|
|
|
|
|
|
|
Patterns behave like double-quoted strings so can contain the same
|
|
|
|
backslash escapes.
|
|
|
|
|
|
|
|
/\s\u{6771 4eac 90fd}/.match("Go to 東京都")
|
|
|
|
#=> #<MatchData " 東京都">
|
|
|
|
|
|
|
|
Arbitrary Ruby expressions can be embedded into patterns with the
|
|
|
|
<tt>#{...}</tt> construct.
|
|
|
|
|
|
|
|
place = "東京都"
|
|
|
|
/#{place}/.match("Go to 東京都")
|
|
|
|
#=> #<MatchData "東京都">
|
|
|
|
|
|
|
|
== Character Classes
|
|
|
|
|
|
|
|
A <i>character class</i> is delimited with square brackets (<tt>[</tt>,
|
|
|
|
<tt>]</tt>) and lists characters that may appear at that point in the
|
|
|
|
match. <tt>/[ab]/</tt> means _a_ or _b_, as opposed to <tt>/ab/</tt> which
|
|
|
|
means _a_ followed by _b_.
|
|
|
|
|
|
|
|
/W[aeiou]rd/.match("Word") #=> #<MatchData "Word">
|
|
|
|
|
|
|
|
Within a character class the hyphen (<tt>-</tt>) is a metacharacter
|
|
|
|
denoting an inclusive range of characters. <tt>[abcd]</tt> is equivalent
|
|
|
|
to <tt>[a-d]</tt>. A range can be followed by another range, so
|
|
|
|
<tt>[abcdwxyz]</tt> is equivalent to <tt>[a-dw-z]</tt>. The order in which
|
|
|
|
ranges or individual characters appear inside a character class is
|
|
|
|
irrelevant.
|
|
|
|
|
|
|
|
/[0-9a-f]/.match('9f') #=> #<MatchData "9">
|
|
|
|
/[9f]/.match('9f') #=> #<MatchData "9">
|
|
|
|
|
|
|
|
If the first character of a character class is a caret (<tt>^</tt>) the
|
|
|
|
class is inverted: it matches any character _except_ those named.
|
|
|
|
|
|
|
|
/[^a-eg-z]/.match('f') #=> #<MatchData "f">
|
|
|
|
|
|
|
|
A character class may contain another character class. By itself this
|
|
|
|
isn't useful because <tt>[a-z[0-9]]</tt> describes the same set as
|
|
|
|
<tt>[a-z0-9]</tt>. However, character classes also support the <tt>&&</tt>
|
|
|
|
operator which performs set intersection on its arguments. The two can be
|
|
|
|
combined as follows:
|
|
|
|
|
|
|
|
/[a-w&&[^c-g]z]/ # ([a-w] AND ([^c-g] OR z))
|
2013-09-16 23:56:32 -04:00
|
|
|
|
|
|
|
This is equivalent to:
|
|
|
|
|
2009-09-17 21:11:55 -04:00
|
|
|
/[abh-w]/
|
|
|
|
|
|
|
|
The following metacharacters also behave like character classes:
|
|
|
|
|
|
|
|
* <tt>/./</tt> - Any character except a newline.
|
|
|
|
* <tt>/./m</tt> - Any character (the +m+ modifier enables multiline mode)
|
|
|
|
* <tt>/\w/</tt> - A word character (<tt>[a-zA-Z0-9_]</tt>)
|
2013-11-12 19:05:38 -05:00
|
|
|
* <tt>/\W/</tt> - A non-word character (<tt>[^a-zA-Z0-9_]</tt>).
|
|
|
|
Please take a look at {Bug #4044}[https://bugs.ruby-lang.org/issues/4044] if
|
|
|
|
using <tt>/\W/</tt> with the <tt>/i</tt> modifier.
|
2009-09-17 21:11:55 -04:00
|
|
|
* <tt>/\d/</tt> - A digit character (<tt>[0-9]</tt>)
|
|
|
|
* <tt>/\D/</tt> - A non-digit character (<tt>[^0-9]</tt>)
|
|
|
|
* <tt>/\h/</tt> - A hexdigit character (<tt>[0-9a-fA-F]</tt>)
|
|
|
|
* <tt>/\H/</tt> - A non-hexdigit character (<tt>[^0-9a-fA-F]</tt>)
|
2015-01-01 07:41:54 -05:00
|
|
|
* <tt>/\s/</tt> - A whitespace character: <tt>/[ \t\r\n\f\v]/</tt>
|
|
|
|
* <tt>/\S/</tt> - A non-whitespace character: <tt>/[^ \t\r\n\f\v]/</tt>
|
2009-09-17 21:11:55 -04:00
|
|
|
|
|
|
|
POSIX <i>bracket expressions</i> are also similar to character classes.
|
|
|
|
They provide a portable alternative to the above, with the added benefit
|
|
|
|
that they encompass non-ASCII characters. For instance, <tt>/\d/</tt>
|
|
|
|
matches only the ASCII decimal digits (0-9); whereas <tt>/[[:digit:]]/</tt>
|
|
|
|
matches any character in the Unicode _Nd_ category.
|
|
|
|
|
|
|
|
* <tt>/[[:alnum:]]/</tt> - Alphabetic and numeric character
|
|
|
|
* <tt>/[[:alpha:]]/</tt> - Alphabetic character
|
|
|
|
* <tt>/[[:blank:]]/</tt> - Space or tab
|
|
|
|
* <tt>/[[:cntrl:]]/</tt> - Control character
|
|
|
|
* <tt>/[[:digit:]]/</tt> - Digit
|
|
|
|
* <tt>/[[:graph:]]/</tt> - Non-blank character (excludes spaces, control
|
|
|
|
characters, and similar)
|
|
|
|
* <tt>/[[:lower:]]/</tt> - Lowercase alphabetical character
|
|
|
|
* <tt>/[[:print:]]/</tt> - Like [:graph:], but includes the space character
|
|
|
|
* <tt>/[[:punct:]]/</tt> - Punctuation character
|
|
|
|
* <tt>/[[:space:]]/</tt> - Whitespace character (<tt>[:blank:]</tt>, newline,
|
2011-12-07 18:22:30 -05:00
|
|
|
carriage return, etc.)
|
2009-09-17 21:11:55 -04:00
|
|
|
* <tt>/[[:upper:]]/</tt> - Uppercase alphabetical
|
|
|
|
* <tt>/[[:xdigit:]]/</tt> - Digit allowed in a hexadecimal number (i.e.,
|
|
|
|
0-9a-fA-F)
|
|
|
|
|
|
|
|
Ruby also supports the following non-POSIX character classes:
|
|
|
|
|
|
|
|
* <tt>/[[:word:]]/</tt> - A character in one of the following Unicode
|
|
|
|
general categories _Letter_, _Mark_, _Number_,
|
2011-12-30 00:55:37 -05:00
|
|
|
<i>Connector_Punctuation</i>
|
2009-09-17 21:11:55 -04:00
|
|
|
* <tt>/[[:ascii:]]/</tt> - A character in the ASCII character set
|
|
|
|
|
|
|
|
# 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">
|
|
|
|
|
|
|
|
== Repetition
|
|
|
|
|
|
|
|
The constructs described so far match a single character. They can be
|
|
|
|
followed by a repetition metacharacter to specify how many times they need
|
|
|
|
to occur. Such metacharacters are called <i>quantifiers</i>.
|
|
|
|
|
|
|
|
* <tt>*</tt> - Zero or more times
|
|
|
|
* <tt>+</tt> - One or more times
|
|
|
|
* <tt>?</tt> - Zero or one times (optional)
|
|
|
|
* <tt>{</tt><i>n</i><tt>}</tt> - Exactly <i>n</i> times
|
|
|
|
* <tt>{</tt><i>n</i><tt>,}</tt> - <i>n</i> or more times
|
|
|
|
* <tt>{,</tt><i>m</i><tt>}</tt> - <i>m</i> or less times
|
|
|
|
* <tt>{</tt><i>n</i><tt>,</tt><i>m</i><tt>}</tt> - At least <i>n</i> and
|
|
|
|
at most <i>m</i> times
|
|
|
|
|
2013-09-16 23:56:32 -04:00
|
|
|
At least one uppercase character ('H'), at least one lowercase character
|
|
|
|
('e'), two 'l' characters, then one 'o':
|
|
|
|
|
2009-09-17 21:11:55 -04:00
|
|
|
"Hello".match(/[[:upper:]]+[[:lower:]]+l{2}o/) #=> #<MatchData "Hello">
|
|
|
|
|
|
|
|
Repetition is <i>greedy</i> by default: as many occurrences as possible
|
|
|
|
are matched while still allowing the overall match to succeed. By
|
|
|
|
contrast, <i>lazy</i> matching makes the minimal amount of matches
|
|
|
|
necessary for overall success. A greedy metacharacter can be made lazy by
|
|
|
|
following it with <tt>?</tt>.
|
|
|
|
|
2013-09-16 23:56:32 -04:00
|
|
|
Both patterns below match the string. The first uses a greedy quantifier so
|
|
|
|
'.+' matches '<a><b>'; the second uses a lazy quantifier so '.+?' matches
|
|
|
|
'<a>':
|
|
|
|
|
2009-09-17 21:11:55 -04:00
|
|
|
/<.+>/.match("<a><b>") #=> #<MatchData "<a><b>">
|
|
|
|
/<.+?>/.match("<a><b>") #=> #<MatchData "<a>">
|
|
|
|
|
|
|
|
A quantifier followed by <tt>+</tt> matches <i>possessively</i>: once it
|
|
|
|
has matched it does not backtrack. They behave like greedy quantifiers,
|
|
|
|
but having matched they refuse to "give up" their match even if this
|
|
|
|
jeopardises the overall match.
|
|
|
|
|
|
|
|
== Capturing
|
|
|
|
|
|
|
|
Parentheses can be used for <i>capturing</i>. The text enclosed by the
|
|
|
|
<i>n</i><sup>th</sup> group of parentheses can be subsequently referred to
|
|
|
|
with <i>n</i>. Within a pattern use the <i>backreference</i>
|
2011-12-07 18:22:30 -05:00
|
|
|
<tt>\n</tt>; outside of the pattern use
|
2009-09-17 21:11:55 -04:00
|
|
|
<tt>MatchData[</tt><i>n</i><tt>]</tt>.
|
|
|
|
|
2013-09-16 23:56:32 -04:00
|
|
|
'at' is captured by the first group of parentheses, then referred to later
|
|
|
|
with <tt>\1</tt>:
|
|
|
|
|
2009-09-17 21:11:55 -04:00
|
|
|
/[csh](..) [csh]\1 in/.match("The cat sat in the hat")
|
|
|
|
#=> #<MatchData "cat sat in" 1:"at">
|
2013-09-16 23:56:32 -04:00
|
|
|
|
|
|
|
Regexp#match returns a MatchData object which makes the captured text
|
|
|
|
available with its #[] method:
|
|
|
|
|
2009-09-17 21:11:55 -04:00
|
|
|
/[csh](..) [csh]\1 in/.match("The cat sat in the hat")[1] #=> 'at'
|
|
|
|
|
|
|
|
Capture groups can be referred to by name when defined with the
|
|
|
|
<tt>(?<</tt><i>name</i><tt>>)</tt> or <tt>(?'</tt><i>name</i><tt>')</tt>
|
|
|
|
constructs.
|
|
|
|
|
|
|
|
/\$(?<dollars>\d+)\.(?<cents>\d+)/.match("$3.67")
|
2017-10-21 19:36:53 -04:00
|
|
|
#=> #<MatchData "$3.67" dollars:"3" cents:"67">
|
2009-09-17 21:11:55 -04:00
|
|
|
/\$(?<dollars>\d+)\.(?<cents>\d+)/.match("$3.67")[:dollars] #=> "3"
|
|
|
|
|
|
|
|
Named groups can be backreferenced with <tt>\k<</tt><i>name</i><tt>></tt>,
|
|
|
|
where _name_ is the group name.
|
|
|
|
|
|
|
|
/(?<vowel>[aeiou]).\k<vowel>.\k<vowel>/.match('ototomy')
|
|
|
|
#=> #<MatchData "ototo" vowel:"o">
|
|
|
|
|
|
|
|
*Note*: A regexp can't use named backreferences and numbered
|
2019-07-26 17:26:59 -04:00
|
|
|
backreferences simultaneously. Also, if a named capture is used in a
|
|
|
|
regexp, then parentheses used for grouping which would otherwise result
|
|
|
|
in a unnamed capture are treated as non-capturing.
|
|
|
|
|
|
|
|
/(\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"}
|
2009-09-17 21:11:55 -04:00
|
|
|
|
|
|
|
When named capture groups are used with a literal regexp on the left-hand
|
|
|
|
side of an expression and the <tt>=~</tt> operator, the captured text is
|
|
|
|
also assigned to local variables with corresponding names.
|
|
|
|
|
|
|
|
/\$(?<dollars>\d+)\.(?<cents>\d+)/ =~ "$3.67" #=> 0
|
|
|
|
dollars #=> "3"
|
|
|
|
|
|
|
|
== Grouping
|
|
|
|
|
|
|
|
Parentheses also <i>group</i> the terms they enclose, allowing them to be
|
|
|
|
quantified as one <i>atomic</i> whole.
|
|
|
|
|
2013-09-16 23:56:32 -04:00
|
|
|
The pattern below matches a vowel followed by 2 word characters:
|
|
|
|
|
2009-09-17 21:11:55 -04:00
|
|
|
/[aeiou]\w{2}/.match("Caenorhabditis elegans") #=> #<MatchData "aen">
|
2013-09-16 23:56:32 -04:00
|
|
|
|
|
|
|
Whereas the following pattern matches a vowel followed by a word character,
|
|
|
|
twice, i.e. <tt>[aeiou]\w[aeiou]\w</tt>: 'enor'.
|
|
|
|
|
2009-09-17 21:11:55 -04:00
|
|
|
/([aeiou]\w){2}/.match("Caenorhabditis elegans")
|
|
|
|
#=> #<MatchData "enor" 1:"or">
|
|
|
|
|
|
|
|
The <tt>(?:</tt>...<tt>)</tt> construct provides grouping without
|
|
|
|
capturing. That is, it combines the terms it contains into an atomic whole
|
|
|
|
without creating a backreference. This benefits performance at the slight
|
2012-07-24 17:54:50 -04:00
|
|
|
expense of readability.
|
2009-09-17 21:11:55 -04:00
|
|
|
|
2013-09-16 23:56:32 -04:00
|
|
|
The first group of parentheses captures 'n' and the second 'ti'. The second
|
|
|
|
group is referred to later with the backreference <tt>\2</tt>:
|
|
|
|
|
2009-09-17 21:11:55 -04:00
|
|
|
/I(n)ves(ti)ga\2ons/.match("Investigations")
|
|
|
|
#=> #<MatchData "Investigations" 1:"n" 2:"ti">
|
2013-09-16 23:56:32 -04:00
|
|
|
|
|
|
|
The first group of parentheses is now made non-capturing with '?:', so it
|
|
|
|
still matches 'n', but doesn't create the backreference. Thus, the
|
|
|
|
backreference <tt>\1</tt> now refers to 'ti'.
|
|
|
|
|
2009-09-17 21:11:55 -04:00
|
|
|
/I(?:n)ves(ti)ga\1ons/.match("Investigations")
|
|
|
|
#=> #<MatchData "Investigations" 1:"ti">
|
|
|
|
|
|
|
|
=== Atomic Grouping
|
|
|
|
|
|
|
|
Grouping can be made <i>atomic</i> with
|
|
|
|
<tt>(?></tt><i>pat</i><tt>)</tt>. This causes the subexpression <i>pat</i>
|
|
|
|
to be matched independently of the rest of the expression such that what
|
|
|
|
it matches becomes fixed for the remainder of the match, unless the entire
|
|
|
|
subexpression must be abandoned and subsequently revisited. In this
|
|
|
|
way <i>pat</i> is treated as a non-divisible whole. Atomic grouping is
|
|
|
|
typically used to optimise patterns so as to prevent the regular
|
2012-07-24 17:54:50 -04:00
|
|
|
expression engine from backtracking needlessly.
|
2009-09-17 21:11:55 -04:00
|
|
|
|
2013-09-16 23:56:32 -04:00
|
|
|
The <tt>"</tt> in the pattern below matches the first character of the string,
|
|
|
|
then <tt>.*</tt> matches <i>Quote"</i>. This causes the overall match to fail,
|
|
|
|
so the text matched by <tt>.*</tt> is backtracked by one position, which
|
|
|
|
leaves the final character of the string available to match <tt>"</tt>
|
|
|
|
|
2009-09-17 21:11:55 -04:00
|
|
|
/".*"/.match('"Quote"') #=> #<MatchData "\"Quote\"">
|
2013-09-16 23:56:32 -04:00
|
|
|
|
|
|
|
If <tt>.*</tt> is grouped atomically, it refuses to backtrack <i>Quote"</i>,
|
|
|
|
even though this means that the overall match fails
|
|
|
|
|
2009-09-17 21:11:55 -04:00
|
|
|
/"(?>.*)"/.match('"Quote"') #=> nil
|
|
|
|
|
|
|
|
== Subexpression Calls
|
|
|
|
|
|
|
|
The <tt>\g<</tt><i>name</i><tt>></tt> syntax matches the previous
|
|
|
|
subexpression named _name_, which can be a group name or number, again.
|
|
|
|
This differs from backreferences in that it re-executes the group rather
|
|
|
|
than simply trying to re-match the same text.
|
|
|
|
|
2013-09-16 23:56:32 -04:00
|
|
|
This pattern matches a <i>(</i> character and assigns it to the <tt>paren</tt>
|
|
|
|
group, tries to call that the <tt>paren</tt> sub-expression again but fails,
|
|
|
|
then matches a literal <i>)</i>:
|
|
|
|
|
2009-09-17 21:11:55 -04:00
|
|
|
/\A(?<paren>\(\g<paren>*\))*\z/ =~ '()'
|
|
|
|
|
|
|
|
|
|
|
|
/\A(?<paren>\(\g<paren>*\))*\z/ =~ '(())' #=> 0
|
|
|
|
# ^1
|
|
|
|
# ^2
|
|
|
|
# ^3
|
|
|
|
# ^4
|
|
|
|
# ^5
|
|
|
|
# ^6
|
|
|
|
# ^7
|
|
|
|
# ^8
|
|
|
|
# ^9
|
|
|
|
# ^10
|
|
|
|
|
|
|
|
1. Matches at the beginning of the string, i.e. before the first
|
|
|
|
character.
|
|
|
|
2. Enters a named capture group called <tt>paren</tt>
|
|
|
|
3. Matches a literal <i>(</i>, the first character in the string
|
|
|
|
4. Calls the <tt>paren</tt> group again, i.e. recurses back to the
|
|
|
|
second step
|
|
|
|
5. Re-enters the <tt>paren</tt> group
|
|
|
|
6. Matches a literal <i>(</i>, the second character in the
|
|
|
|
string
|
|
|
|
7. Try to call <tt>paren</tt> a third time, but fail because
|
|
|
|
doing so would prevent an overall successful match
|
|
|
|
8. Match a literal <i>)</i>, the third character in the string.
|
|
|
|
Marks the end of the second recursive call
|
|
|
|
9. Match a literal <i>)</i>, the fourth character in the string
|
|
|
|
10. Match the end of the string
|
|
|
|
|
|
|
|
== Alternation
|
|
|
|
|
|
|
|
The vertical bar metacharacter (<tt>|</tt>) combines two expressions into
|
|
|
|
a single one that matches either of the expressions. Each expression is an
|
|
|
|
<i>alternative</i>.
|
|
|
|
|
|
|
|
/\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
|
|
|
|
|
|
|
|
== Character Properties
|
|
|
|
|
|
|
|
The <tt>\p{}</tt> construct matches characters with the named property,
|
|
|
|
much like POSIX bracket classes.
|
|
|
|
|
|
|
|
* <tt>/\p{Alnum}/</tt> - Alphabetic and numeric character
|
|
|
|
* <tt>/\p{Alpha}/</tt> - Alphabetic character
|
|
|
|
* <tt>/\p{Blank}/</tt> - Space or tab
|
|
|
|
* <tt>/\p{Cntrl}/</tt> - Control character
|
|
|
|
* <tt>/\p{Digit}/</tt> - Digit
|
|
|
|
* <tt>/\p{Graph}/</tt> - Non-blank character (excludes spaces, control
|
|
|
|
characters, and similar)
|
|
|
|
* <tt>/\p{Lower}/</tt> - Lowercase alphabetical character
|
|
|
|
* <tt>/\p{Print}/</tt> - Like <tt>\p{Graph}</tt>, but includes the space character
|
|
|
|
* <tt>/\p{Punct}/</tt> - Punctuation character
|
|
|
|
* <tt>/\p{Space}/</tt> - Whitespace character (<tt>[:blank:]</tt>, newline,
|
|
|
|
carriage return, etc.)
|
|
|
|
* <tt>/\p{Upper}/</tt> - Uppercase alphabetical
|
|
|
|
* <tt>/\p{XDigit}/</tt> - Digit allowed in a hexadecimal number (i.e., 0-9a-fA-F)
|
|
|
|
* <tt>/\p{Word}/</tt> - A member of one of the following Unicode general
|
|
|
|
category <i>Letter</i>, <i>Mark</i>, <i>Number</i>,
|
|
|
|
<i>Connector\_Punctuation</i>
|
|
|
|
* <tt>/\p{ASCII}/</tt> - A character in the ASCII character set
|
|
|
|
* <tt>/\p{Any}/</tt> - Any Unicode character (including unassigned
|
|
|
|
characters)
|
|
|
|
* <tt>/\p{Assigned}/</tt> - An assigned character
|
|
|
|
|
|
|
|
A Unicode character's <i>General Category</i> value can also be matched
|
|
|
|
with <tt>\p{</tt><i>Ab</i><tt>}</tt> where <i>Ab</i> is the category's
|
|
|
|
abbreviation as described below:
|
|
|
|
|
|
|
|
* <tt>/\p{L}/</tt> - 'Letter'
|
|
|
|
* <tt>/\p{Ll}/</tt> - 'Letter: Lowercase'
|
|
|
|
* <tt>/\p{Lm}/</tt> - 'Letter: Mark'
|
|
|
|
* <tt>/\p{Lo}/</tt> - 'Letter: Other'
|
|
|
|
* <tt>/\p{Lt}/</tt> - 'Letter: Titlecase'
|
|
|
|
* <tt>/\p{Lu}/</tt> - 'Letter: Uppercase
|
|
|
|
* <tt>/\p{Lo}/</tt> - 'Letter: Other'
|
|
|
|
* <tt>/\p{M}/</tt> - 'Mark'
|
|
|
|
* <tt>/\p{Mn}/</tt> - 'Mark: Nonspacing'
|
|
|
|
* <tt>/\p{Mc}/</tt> - 'Mark: Spacing Combining'
|
|
|
|
* <tt>/\p{Me}/</tt> - 'Mark: Enclosing'
|
|
|
|
* <tt>/\p{N}/</tt> - 'Number'
|
|
|
|
* <tt>/\p{Nd}/</tt> - 'Number: Decimal Digit'
|
|
|
|
* <tt>/\p{Nl}/</tt> - 'Number: Letter'
|
|
|
|
* <tt>/\p{No}/</tt> - 'Number: Other'
|
|
|
|
* <tt>/\p{P}/</tt> - 'Punctuation'
|
|
|
|
* <tt>/\p{Pc}/</tt> - 'Punctuation: Connector'
|
|
|
|
* <tt>/\p{Pd}/</tt> - 'Punctuation: Dash'
|
|
|
|
* <tt>/\p{Ps}/</tt> - 'Punctuation: Open'
|
|
|
|
* <tt>/\p{Pe}/</tt> - 'Punctuation: Close'
|
|
|
|
* <tt>/\p{Pi}/</tt> - 'Punctuation: Initial Quote'
|
|
|
|
* <tt>/\p{Pf}/</tt> - 'Punctuation: Final Quote'
|
|
|
|
* <tt>/\p{Po}/</tt> - 'Punctuation: Other'
|
|
|
|
* <tt>/\p{S}/</tt> - 'Symbol'
|
|
|
|
* <tt>/\p{Sm}/</tt> - 'Symbol: Math'
|
|
|
|
* <tt>/\p{Sc}/</tt> - 'Symbol: Currency'
|
|
|
|
* <tt>/\p{Sc}/</tt> - 'Symbol: Currency'
|
|
|
|
* <tt>/\p{Sk}/</tt> - 'Symbol: Modifier'
|
|
|
|
* <tt>/\p{So}/</tt> - 'Symbol: Other'
|
|
|
|
* <tt>/\p{Z}/</tt> - 'Separator'
|
|
|
|
* <tt>/\p{Zs}/</tt> - 'Separator: Space'
|
|
|
|
* <tt>/\p{Zl}/</tt> - 'Separator: Line'
|
|
|
|
* <tt>/\p{Zp}/</tt> - 'Separator: Paragraph'
|
|
|
|
* <tt>/\p{C}/</tt> - 'Other'
|
|
|
|
* <tt>/\p{Cc}/</tt> - 'Other: Control'
|
|
|
|
* <tt>/\p{Cf}/</tt> - 'Other: Format'
|
|
|
|
* <tt>/\p{Cn}/</tt> - 'Other: Not Assigned'
|
|
|
|
* <tt>/\p{Co}/</tt> - 'Other: Private Use'
|
|
|
|
* <tt>/\p{Cs}/</tt> - 'Other: Surrogate'
|
|
|
|
|
|
|
|
Lastly, <tt>\p{}</tt> matches a character's Unicode <i>script</i>. The
|
|
|
|
following scripts are supported: <i>Arabic</i>, <i>Armenian</i>,
|
|
|
|
<i>Balinese</i>, <i>Bengali</i>, <i>Bopomofo</i>, <i>Braille</i>,
|
|
|
|
<i>Buginese</i>, <i>Buhid</i>, <i>Canadian_Aboriginal</i>, <i>Carian</i>,
|
|
|
|
<i>Cham</i>, <i>Cherokee</i>, <i>Common</i>, <i>Coptic</i>,
|
|
|
|
<i>Cuneiform</i>, <i>Cypriot</i>, <i>Cyrillic</i>, <i>Deseret</i>,
|
|
|
|
<i>Devanagari</i>, <i>Ethiopic</i>, <i>Georgian</i>, <i>Glagolitic</i>,
|
|
|
|
<i>Gothic</i>, <i>Greek</i>, <i>Gujarati</i>, <i>Gurmukhi</i>, <i>Han</i>,
|
|
|
|
<i>Hangul</i>, <i>Hanunoo</i>, <i>Hebrew</i>, <i>Hiragana</i>,
|
|
|
|
<i>Inherited</i>, <i>Kannada</i>, <i>Katakana</i>, <i>Kayah_Li</i>,
|
|
|
|
<i>Kharoshthi</i>, <i>Khmer</i>, <i>Lao</i>, <i>Latin</i>, <i>Lepcha</i>,
|
|
|
|
<i>Limbu</i>, <i>Linear_B</i>, <i>Lycian</i>, <i>Lydian</i>,
|
|
|
|
<i>Malayalam</i>, <i>Mongolian</i>, <i>Myanmar</i>, <i>New_Tai_Lue</i>,
|
|
|
|
<i>Nko</i>, <i>Ogham</i>, <i>Ol_Chiki</i>, <i>Old_Italic</i>,
|
|
|
|
<i>Old_Persian</i>, <i>Oriya</i>, <i>Osmanya</i>, <i>Phags_Pa</i>,
|
|
|
|
<i>Phoenician</i>, <i>Rejang</i>, <i>Runic</i>, <i>Saurashtra</i>,
|
|
|
|
<i>Shavian</i>, <i>Sinhala</i>, <i>Sundanese</i>, <i>Syloti_Nagri</i>,
|
|
|
|
<i>Syriac</i>, <i>Tagalog</i>, <i>Tagbanwa</i>, <i>Tai_Le</i>,
|
|
|
|
<i>Tamil</i>, <i>Telugu</i>, <i>Thaana</i>, <i>Thai</i>, <i>Tibetan</i>,
|
|
|
|
<i>Tifinagh</i>, <i>Ugaritic</i>, <i>Vai</i>, and <i>Yi</i>.
|
|
|
|
|
2013-09-16 23:56:32 -04:00
|
|
|
Unicode codepoint U+06E9 is named "ARABIC PLACE OF SAJDAH" and belongs to the
|
|
|
|
Arabic script:
|
|
|
|
|
2009-09-17 21:11:55 -04:00
|
|
|
/\p{Arabic}/.match("\u06E9") #=> #<MatchData "\u06E9">
|
|
|
|
|
|
|
|
All character properties can be inverted by prefixing their name with a
|
|
|
|
caret (<tt>^</tt>).
|
|
|
|
|
2013-09-16 23:56:32 -04:00
|
|
|
Letter 'A' is not in the Unicode Ll (Letter; Lowercase) category, so this
|
|
|
|
match succeeds:
|
|
|
|
|
2009-09-17 21:11:55 -04:00
|
|
|
/\p{^Ll}/.match("A") #=> #<MatchData "A">
|
|
|
|
|
|
|
|
== Anchors
|
|
|
|
|
|
|
|
Anchors are metacharacter that match the zero-width positions between
|
|
|
|
characters, <i>anchoring</i> the match to a specific position.
|
|
|
|
|
|
|
|
* <tt>^</tt> - Matches beginning of line
|
|
|
|
* <tt>$</tt> - Matches end of line
|
|
|
|
* <tt>\A</tt> - Matches beginning of string.
|
|
|
|
* <tt>\Z</tt> - Matches end of string. If string ends with a newline,
|
|
|
|
it matches just before newline
|
|
|
|
* <tt>\z</tt> - Matches end of string
|
2016-01-08 08:34:52 -05:00
|
|
|
* <tt>\G</tt> - Matches first matching position:
|
|
|
|
|
|
|
|
In methods like <tt>String#gsub</tt> and <tt>String#scan</tt>, it changes on each iteration.
|
|
|
|
It initially matches the beginning of subject, and in each following iteration it matches where the last match finished.
|
|
|
|
|
|
|
|
" a b c".gsub(/ /, '_') #=> "____a_b_c"
|
|
|
|
" a b c".gsub(/\G /, '_') #=> "____a b c"
|
|
|
|
|
|
|
|
In methods like <tt>Regexp#match</tt> and <tt>String#match</tt> that take an (optional) offset, it matches where the search begins.
|
|
|
|
|
|
|
|
"hello, world".match(/,/, 3) #=> #<MatchData ",">
|
|
|
|
"hello, world".match(/\G,/, 3) #=> nil
|
|
|
|
|
2011-12-30 00:55:37 -05:00
|
|
|
* <tt>\b</tt> - Matches word boundaries when outside brackets;
|
|
|
|
backspace (0x08) when inside brackets
|
2009-09-17 21:11:55 -04:00
|
|
|
* <tt>\B</tt> - Matches non-word boundaries
|
|
|
|
* <tt>(?=</tt><i>pat</i><tt>)</tt> - <i>Positive lookahead</i> assertion:
|
|
|
|
ensures that the following characters match <i>pat</i>, but doesn't
|
|
|
|
include those characters in the matched text
|
|
|
|
* <tt>(?!</tt><i>pat</i><tt>)</tt> - <i>Negative lookahead</i> assertion:
|
|
|
|
ensures that the following characters do not match <i>pat</i>, but
|
|
|
|
doesn't include those characters in the matched text
|
|
|
|
* <tt>(?<=</tt><i>pat</i><tt>)</tt> - <i>Positive lookbehind</i>
|
|
|
|
assertion: ensures that the preceding characters match <i>pat</i>, but
|
|
|
|
doesn't include those characters in the matched text
|
|
|
|
* <tt>(?<!</tt><i>pat</i><tt>)</tt> - <i>Negative lookbehind</i>
|
|
|
|
assertion: ensures that the preceding characters do not match
|
|
|
|
<i>pat</i>, but doesn't include those characters in the matched text
|
|
|
|
|
2013-09-16 23:56:32 -04:00
|
|
|
If a pattern isn't anchored it can begin at any point in the string:
|
|
|
|
|
2009-09-17 21:11:55 -04:00
|
|
|
/real/.match("surrealist") #=> #<MatchData "real">
|
2013-09-16 23:56:32 -04:00
|
|
|
|
|
|
|
Anchoring the pattern to the beginning of the string forces the match to start
|
|
|
|
there. 'real' doesn't occur at the beginning of the string, so now the match
|
|
|
|
fails:
|
|
|
|
|
2009-09-17 21:11:55 -04:00
|
|
|
/\Areal/.match("surrealist") #=> nil
|
2013-09-16 23:56:32 -04:00
|
|
|
|
|
|
|
The match below fails because although 'Demand' contains 'and', the pattern
|
|
|
|
does not occur at a word boundary.
|
|
|
|
|
2009-09-17 21:11:55 -04:00
|
|
|
/\band/.match("Demand")
|
2013-09-16 23:56:32 -04:00
|
|
|
|
|
|
|
Whereas in the following example 'and' has been anchored to a non-word
|
|
|
|
boundary so instead of matching the first 'and' it matches from the fourth
|
|
|
|
letter of 'demand' instead:
|
|
|
|
|
2009-09-17 21:11:55 -04:00
|
|
|
/\Band.+/.match("Supply and demand curve") #=> #<MatchData "and curve">
|
2013-09-16 23:56:32 -04:00
|
|
|
|
|
|
|
The pattern below uses positive lookahead and positive lookbehind to match
|
|
|
|
text appearing in <b></b> tags without including the tags in the match:
|
|
|
|
|
2009-09-17 21:11:55 -04:00
|
|
|
/(?<=<b>)\w+(?=<\/b>)/.match("Fortune favours the <b>bold</b>")
|
|
|
|
#=> #<MatchData "bold">
|
|
|
|
|
|
|
|
== Options
|
|
|
|
|
|
|
|
The end delimiter for a regexp can be followed by one or more single-letter
|
|
|
|
options which control how the pattern can match.
|
|
|
|
|
|
|
|
* <tt>/pat/i</tt> - Ignore case
|
|
|
|
* <tt>/pat/m</tt> - Treat a newline as a character matched by <tt>.</tt>
|
|
|
|
* <tt>/pat/x</tt> - Ignore whitespace and comments in the pattern
|
|
|
|
* <tt>/pat/o</tt> - Perform <tt>#{}</tt> interpolation only once
|
|
|
|
|
|
|
|
<tt>i</tt>, <tt>m</tt>, and <tt>x</tt> can also be applied on the
|
|
|
|
subexpression level with the
|
|
|
|
<tt>(?</tt><i>on</i><tt>-</tt><i>off</i><tt>)</tt> construct, which
|
|
|
|
enables options <i>on</i>, and disables options <i>off</i> for the
|
2018-10-21 02:42:53 -04:00
|
|
|
expression enclosed by the parentheses:
|
2009-09-17 21:11:55 -04:00
|
|
|
|
2018-10-21 02:42:53 -04:00
|
|
|
/a(?i:b)c/.match('aBc') #=> #<MatchData "aBc">
|
|
|
|
/a(?-i:b)c/i.match('ABC') #=> nil
|
|
|
|
|
|
|
|
Additionally, these options can also be toggled for the remainder of the
|
|
|
|
pattern:
|
|
|
|
|
|
|
|
/a(?i)bc/.match('abC') #=> #<MatchData "abC">
|
2009-09-17 21:11:55 -04:00
|
|
|
|
2011-12-07 18:22:30 -05:00
|
|
|
Options may also be used with <tt>Regexp.new</tt>:
|
|
|
|
|
|
|
|
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
|
|
|
|
|
2009-09-17 21:11:55 -04:00
|
|
|
== Free-Spacing Mode and Comments
|
|
|
|
|
|
|
|
As mentioned above, the <tt>x</tt> option enables <i>free-spacing</i>
|
|
|
|
mode. Literal white space inside the pattern is ignored, and the
|
|
|
|
octothorpe (<tt>#</tt>) character introduces a comment until the end of
|
2014-04-28 11:20:28 -04:00
|
|
|
the line. This allows the components of the pattern to be organized in a
|
2009-09-17 21:11:55 -04:00
|
|
|
potentially more readable fashion.
|
|
|
|
|
2013-09-16 23:56:32 -04:00
|
|
|
A contrived pattern to match a number with optional decimal places:
|
|
|
|
|
2009-09-17 21:11:55 -04:00
|
|
|
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">
|
|
|
|
|
2014-05-24 21:36:05 -04:00
|
|
|
There are a number of strategies for matching whitespace:
|
|
|
|
|
|
|
|
* Use a pattern such as <tt>\s</tt> or <tt>\p{Space}</tt>.
|
|
|
|
* Use escaped whitespace such as <tt>\ </tt>, i.e. a space preceded by a backslash.
|
|
|
|
* Use a character class such as <tt>[ ]</tt>.
|
2009-09-17 21:11:55 -04:00
|
|
|
|
|
|
|
Comments can be included in a non-<tt>x</tt> pattern with the
|
|
|
|
<tt>(?#</tt><i>comment</i><tt>)</tt> construct, where <i>comment</i> is
|
|
|
|
arbitrary text ignored by the regexp engine.
|
|
|
|
|
2016-04-07 06:04:05 -04:00
|
|
|
Comments in regexp literals cannot include unescaped terminator
|
|
|
|
characters.
|
|
|
|
|
2009-09-17 21:11:55 -04:00
|
|
|
== Encoding
|
|
|
|
|
|
|
|
Regular expressions are assumed to use the source encoding. This can be
|
|
|
|
overridden with one of the following modifiers.
|
|
|
|
|
|
|
|
* <tt>/</tt><i>pat</i><tt>/u</tt> - UTF-8
|
|
|
|
* <tt>/</tt><i>pat</i><tt>/e</tt> - EUC-JP
|
|
|
|
* <tt>/</tt><i>pat</i><tt>/s</tt> - Windows-31J
|
|
|
|
* <tt>/</tt><i>pat</i><tt>/n</tt> - ASCII-8BIT
|
|
|
|
|
|
|
|
A regexp can be matched against a string when they either share an
|
|
|
|
encoding, or the regexp's encoding is _US-ASCII_ and the string's encoding
|
|
|
|
is ASCII-compatible.
|
|
|
|
|
|
|
|
If a match between incompatible encodings is attempted an
|
|
|
|
<tt>Encoding::CompatibilityError</tt> exception is raised.
|
|
|
|
|
|
|
|
The <tt>Regexp#fixed_encoding?</tt> predicate indicates whether the regexp
|
|
|
|
has a <i>fixed</i> encoding, that is one incompatible with ASCII. A
|
|
|
|
regexp's encoding can be explicitly fixed by supplying
|
|
|
|
<tt>Regexp::FIXEDENCODING</tt> as the second argument of
|
|
|
|
<tt>Regexp.new</tt>:
|
|
|
|
|
|
|
|
r = Regexp.new("a".force_encoding("iso-8859-1"),Regexp::FIXEDENCODING)
|
2017-10-21 19:36:53 -04:00
|
|
|
r =~ "a\u3042"
|
|
|
|
# raises Encoding::CompatibilityError: incompatible encoding regexp match
|
|
|
|
# (ISO-8859-1 regexp with UTF-8 string)
|
2009-09-17 21:11:55 -04:00
|
|
|
|
2011-12-07 18:22:30 -05:00
|
|
|
== Special global variables
|
|
|
|
|
|
|
|
Pattern matching sets some global variables :
|
|
|
|
* <tt>$~</tt> is equivalent to Regexp.last_match;
|
|
|
|
* <tt>$&</tt> contains the complete matched text;
|
|
|
|
* <tt>$`</tt> contains string before match;
|
|
|
|
* <tt>$'</tt> contains string after match;
|
|
|
|
* <tt>$1</tt>, <tt>$2</tt> and so on contain text matching first, second, etc
|
|
|
|
capture group;
|
|
|
|
* <tt>$+</tt> contains last capture group.
|
|
|
|
|
|
|
|
Example:
|
|
|
|
|
|
|
|
m = /s(\w{2}).*(c)/.match('haystack') #=> #<MatchData "stac" 1:"ta" 2:"c">
|
|
|
|
$~ #=> #<MatchData "stac" 1:"ta" 2:"c">
|
2013-11-12 20:56:27 -05:00
|
|
|
Regexp.last_match #=> #<MatchData "stac" 1:"ta" 2:"c">
|
2011-12-07 18:22:30 -05:00
|
|
|
|
|
|
|
$& #=> "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]
|
|
|
|
|
2012-07-24 17:54:50 -04:00
|
|
|
These global variables are thread-local and method-local variables.
|
2011-12-07 18:22:30 -05:00
|
|
|
|
2009-09-17 21:11:55 -04:00
|
|
|
== Performance
|
|
|
|
|
|
|
|
Certain pathological combinations of constructs can lead to abysmally bad
|
|
|
|
performance.
|
|
|
|
|
|
|
|
Consider a string of 25 <i>a</i>s, a <i>d</i>, 4 <i>a</i>s, and a
|
|
|
|
<i>c</i>.
|
|
|
|
|
2011-12-07 18:02:58 -05:00
|
|
|
s = 'a' * 25 + 'd' + 'a' * 4 + 'c'
|
|
|
|
#=> "aaaaaaaaaaaaaaaaaaaaaaaaadaaaac"
|
2009-09-17 21:11:55 -04:00
|
|
|
|
|
|
|
The following patterns match instantly as you would expect:
|
|
|
|
|
|
|
|
/(b|a)/ =~ s #=> 0
|
|
|
|
/(b|a+)/ =~ s #=> 0
|
2013-07-20 09:34:34 -04:00
|
|
|
/(b|a+)*/ =~ s #=> 0
|
2009-09-17 21:11:55 -04:00
|
|
|
|
|
|
|
However, the following pattern takes appreciably longer:
|
|
|
|
|
2011-12-07 18:02:58 -05:00
|
|
|
/(b|a+)*c/ =~ s #=> 26
|
2009-09-17 21:11:55 -04:00
|
|
|
|
|
|
|
This happens because an atom in the regexp is quantified by both an
|
|
|
|
immediate <tt>+</tt> and an enclosing <tt>*</tt> with nothing to
|
|
|
|
differentiate which is in control of any particular character. The
|
|
|
|
nondeterminism that results produces super-linear performance. (Consult
|
|
|
|
<i>Mastering Regular Expressions</i> (3rd ed.), pp 222, by
|
|
|
|
<i>Jeffery Friedl</i>, for an in-depth analysis). This particular case
|
|
|
|
can be fixed by use of atomic grouping, which prevents the unnecessary
|
|
|
|
backtracking:
|
|
|
|
|
|
|
|
(start = Time.now) && /(b|a+)*c/ =~ s && (Time.now - start)
|
|
|
|
#=> 24.702736882
|
|
|
|
(start = Time.now) && /(?>b|a+)*c/ =~ s && (Time.now - start)
|
|
|
|
#=> 0.000166571
|
|
|
|
|
|
|
|
A similar case is typified by the following example, which takes
|
|
|
|
approximately 60 seconds to execute for me:
|
|
|
|
|
2013-09-16 23:56:32 -04:00
|
|
|
Match a string of 29 <i>a</i>s against a pattern of 29 optional <i>a</i>s
|
|
|
|
followed by 29 mandatory <i>a</i>s:
|
|
|
|
|
2009-09-17 21:11:55 -04:00
|
|
|
Regexp.new('a?' * 29 + 'a' * 29) =~ 'a' * 29
|
|
|
|
|
|
|
|
The 29 optional <i>a</i>s match the string, but this prevents the 29
|
|
|
|
mandatory <i>a</i>s that follow from matching. Ruby must then backtrack
|
|
|
|
repeatedly so as to satisfy as many of the optional matches as it can
|
|
|
|
while still matching the mandatory 29. It is plain to us that none of the
|
|
|
|
optional matches can succeed, but this fact unfortunately eludes Ruby.
|
|
|
|
|
2012-06-01 18:05:46 -04:00
|
|
|
The best way to improve performance is to significantly reduce the amount of
|
|
|
|
backtracking needed. For this case, instead of individually matching 29
|
|
|
|
optional <i>a</i>s, a range of optional <i>a</i>s can be matched all at once
|
|
|
|
with <i>a{0,29}</i>:
|
2009-09-17 21:11:55 -04:00
|
|
|
|
2012-06-01 18:05:46 -04:00
|
|
|
Regexp.new('a{0,29}' + 'a' * 29) =~ 'a' * 29
|
2010-04-10 02:36:13 -04:00
|
|
|
|