[regex] What is the difference between square brackets and parentheses in a regex?

Here is a regular expression I created to use in JavaScript:

var reg_num = /^(7|8|9)\d{9}$/

Here is another one suggested by my team member.

var reg_num = /^[7|8|9][\d]{9}$/

The rule is to validate a phone number:

  • It should be of only ten numbers.
  • The first number is supposed to be any of 7, 8 or 9.

This question is related to regex

The answer is


The first 2 examples act very differently if you are REPLACING them by something. If you match on this:

str = str.replace(/^(7|8|9)/ig,''); 

you would replace 7 or 8 or 9 by the empty string.

If you match on this

str = str.replace(/^[7|8|9]/ig,''); 

you will replace 7 or 8 or 9 OR THE VERTICAL BAR!!!! by the empty string.

I just found this out the hard way.


Your team's advice is almost right, except for the mistake that was made. Once you find out why, you will never forget it. Take a look at this mistake.

/^(7|8|9)\d{9}$/

What this does:

  • ^ and $ denotes anchored matches, which asserts that the subpattern in between these anchors are the entire match. The string will only match if the subpattern matches the entirety of it, not just a section.
  • () denotes a capturing group.
  • 7|8|9 denotes matching either of 7, 8, or 9. It does this with alternations, which is what the pipe operator | does — alternating between alternations. This backtracks between alternations: If the first alternation is not matched, the engine has to return before the pointer location moved during the match of the alternation, to continue matching the next alternation; Whereas the character class can advance sequentially. See this match on a regex engine with optimizations disabled:
Pattern: (r|f)at
Match string: carat

alternations

Pattern: [rf]at
Match string: carat

class

  • \d{9} matches nine digits. \d is a shorthanded metacharacter, which matches any digits.
/^[7|8|9][\d]{9}$/

Look at what it does:

  • ^ and $ denotes anchored matches as well.
  • [7|8|9] is a character class. Any characters from the list 7, |, 8, |, or 9 can be matched, thus the | was added in incorrectly. This matches without backtracking.
  • [\d] is a character class that inhabits the metacharacter \d. The combination of the use of a character class and a single metacharacter is a bad idea, by the way, since the layer of abstraction can slow down the match, but this is only an implementation detail and only applies to a few of regex implementations. JavaScript is not one, but it does make the subpattern slightly longer.
  • {9} indicates the previous single construct is repeated nine times in total.

The optimal regex is /^[789]\d{9}$/, because /^(7|8|9)\d{9}$/ captures unnecessarily which imposes a performance decrease on most regex implementations ( happens to be one, considering the question uses keyword var in code, this probably is JavaScript). The use of which runs on PCRE for preg matching will optimize away the lack of backtracking, however we're not in PHP either, so using classes [] instead of alternations | gives performance bonus as the match does not backtrack, and therefore both matches and fails faster than using your previous regular expression.