RX

Quantifiers

Regular Expressions · 9 entries

* Zero or More

syntax
a*  matches zero or more 'a' characters
example
JS:  'caaandy'.match(/ca*/)
Py:  re.findall(r'bo*', 'A bird booed and a bobcat roared')
output
JS: "caaa"
Py: ["boo", "bo", "b"]

Note * is greedy by default -- it matches as many characters as possible. Beware: .* can match empty strings and lead to unexpected results at every position in a string.

+ One or More

syntax
a+  matches one or more 'a' characters
example
JS:  'caaandy candy cdy'.match(/ca+ndy/g)
Py:  re.findall(r'\d+', 'There are 12 cats and 340 dogs')
output
JS: ["caaandy", "candy"]
Py: ["12", "340"]

Note Unlike *, the + quantifier requires at least one occurrence. This is the most common quantifier for extracting sequences of digits, letters, etc.

? Optional (Zero or One)

syntax
a?  matches zero or one 'a'
example
JS:  'color colour'.match(/colou?r/g)
Py:  re.findall(r'https?://', 'http://a.com and https://b.com')
output
JS: ["color", "colour"]
Py: ["http://", "https://"]

Note Makes the preceding element optional. Extremely useful for handling spelling variations and optional protocol prefixes.

{n} Exact Count

syntax
a{3}  matches exactly 3 'a' characters
example
JS:  '1234-5678-9012-3456'.match(/\d{4}/g)
Py:  re.findall(r'\b[A-Z]{2}\b', 'Go to NY or LA, not NYC')
output
JS: ["1234", "5678", "9012", "3456"]
Py: ["NY", "LA"]

Note Matches the preceding element exactly n times. Useful for fixed-width fields like credit card groups, zip codes, and two-letter state codes.

{n,m} Range Count

syntax
a{2,4}  matches 2 to 4 'a' characters
example
JS:  'aaa aa aaaa a aaaaa'.match(/\ba{2,4}\b/g)
Py:  re.findall(r'\b\w{3,5}\b', 'I am a regex pro now')
output
JS: ["aaa", "aa", "aaaa"]
Py: ["regex", "pro", "now"]

Note Greedy by default -- matches the maximum count first, then backtracks. {n,} means n or more with no upper limit. {0,1} is equivalent to ?.

{n,} Minimum Count

syntax
a{3,}  matches 3 or more 'a' characters
example
JS:  'password1234'.match(/.{8,}/)
Py:  re.search(r'\d{3,}', 'My code is 42 or maybe 12345')
output
JS: "password1234"  (8+ chars)
Py: "12345"  (3+ digits)

Note Useful for minimum-length validation. Remember this is greedy -- it will consume as much as possible.

Greedy vs Lazy Matching

syntax
Greedy: .*  .+  .?     Lazy: .*?  .+?  .??
example
JS:  '<b>bold</b> and <b>more</b>'.match(/<b>.*<\/b>/)
JS:  '<b>bold</b> and <b>more</b>'.match(/<b>.*?<\/b>/)
output
Greedy: "<b>bold</b> and <b>more</b>"  (longest match)
Lazy:   "<b>bold</b>"  (shortest match)

Note Greedy quantifiers eat as much as possible, then backtrack. Lazy quantifiers match as little as possible, then expand. This distinction is critical when parsing HTML, quoted strings, or any delimited content. Always prefer lazy when you want the nearest closing delimiter.

Lazy Quantifier Variants

syntax
*?  +?  ??  {n,m}?  {n,}?
example
JS:  '"hello" and "world"'.match(/"[^"]+?"/g)
Py:  re.findall(r'\d{2,4}?', '12345')
output
JS: ['"hello"', '"world"']
Py: ["12", "34"]  (minimum 2 digits each time)

Note Append ? after any quantifier to make it lazy. {2,4}? prefers matching 2 characters instead of 4. For the quoted-string case, a negated class [^"]+ is often more efficient than a lazy .+? approach.

Possessive Quantifiers (Concept)

syntax
*+  ++  ?+  {n,m}+   (not supported in JS or Python re)
example
Java/PCRE:  '"hello"'.match(/"[^"]*+"/)
Python alternative: use atomic group via regex module
  import regex
  regex.search(r'"(?>"[^"]*)"+', text)
output
Matches "hello" without backtracking into the quoted content

Note Possessive quantifiers never give back characters once matched. They prevent catastrophic backtracking but cause a match to fail if the rest of the pattern cannot match. Not available in JavaScript or Python's built-in re module -- use the third-party 'regex' module in Python or atomic groups in PCRE.