4.5 KiB
Bracket groups
A part of the pattern can be enclosed in parentheses pattern:(...)
. That's called a "capturing group".
That has two effects:
- It allows to place a part of the match into a separate array item when using String#match or RegExp#exec methods.
- If we put a quantifier after the parentheses, it applies to the parentheses as a whole, not the last character.
[cut]
Example
In the example below the pattern pattern:(go)+
finds one or more match:'go'
:
alert( 'Gogogo now!'.match(/(go)+/i) ); // "Gogogo"
Without parentheses, the pattern pattern:/go+/
means subject:g
, followed by subject:o
repeated one or more times. For instance, match:goooo
or match:gooooooooo
.
Parentheses group the word pattern:(go)
together.
Contents of parentheses
Parentheses are numbered from left to right. The search engine remembers the content of each and allows to reference it in the pattern or in the replacement string.
For instance, we can find an HTML-tag using a (simplified) pattern pattern:<.*?>
. Usually we'd want to do something with the result after it.
If we enclose the inner contents of <...>
into parentheses, then we can access it like this:
let str = '<h1>Hello, world!</h1>';
let reg = /<(.*?)>/;
alert( str.match(reg) ); // Array: ["<h1>", "h1"]
The call to String#match returns groups only if the regexp has no pattern:/.../g
flag.
If we need all matches with their groups then we can use RegExp#exec method as described in info:regexp-methods:
let str = '<h1>Hello, world!</h1>';
// two matches: opening <h1> and closing </h1> tags
let reg = /<(.*?)>/g;
let match;
while (match = reg.exec(str)) {
// first shows the match: <h1>,h1
// then shows the match: </h1>,/h1
alert(match);
}
Here we have two matches for pattern:<(.*?)>
, each of them is an array with the full match and groups.
Nested groups
Parentheses can be nested. In this case the numbering also goes from left to right.
For instance, when searching a tag in subject:<span class="my">
we may be interested in:
- The tag content as a whole:
match:span class="my"
. - The tag name:
match:span
. - The tag attributes:
match:class="my"
.
Let's add parentheses for them:
let str = '<span class="my">';
let reg = /<(([a-z]+)\s*([^>]*))>/;
let result = str.match(reg);
alert(result); // <span class="my">, span class="my", span, class="my"
Here's how groups look:
At the zero index of the result
is always the full match.
Then groups, numbered from left to right. Whichever opens first gives the first group result[1]
. Here it encloses the whole tag content.
Then in result[2]
goes the group from the second opening pattern:(
till the corresponding pattern:)
-- tag name, then we don't group spaces, but group attributes for result[3]
.
If a group is optional and doesn't exist in the match, the corresponding result
index is present (and equals undefined
).
For instance, let's consider the regexp pattern:a(z)?(c)?
. It looks for "a"
optionally followed by "z"
optionally followed by "c"
.
If we run it on the string with a single letter subject:a
, then the result is:
let match = 'a'.match(/a(z)?(c)?/);
alert( match.length ); // 3
alert( match[0] ); // a (whole match)
alert( match[1] ); // undefined
alert( match[2] ); // undefined
The array has the length of 3
, but all groups are empty.
And here's a more complex match for the string subject:ack
:
let match = 'ack'.match(/a(z)?(c)?/)
alert( match.length ); // 3
alert( match[0] ); // ac (whole match)
alert( match[1] ); // undefined, because there's nothing for (z)?
alert( match[2] ); // c
The array length is permanent: 3
. But there's nothing for the group pattern:(z)?
, so the result is ["ac", undefined, "c"]
.
Non-capturing groups with ?:
Sometimes we need parentheses to correctly apply a quantifier, but we don't want their contents in the array.
A group may be excluded by adding pattern:?:
in the beginning.
For instance, if we want to find pattern:(go)+
, but don't want to put remember the contents (go
) in a separate array item, we can write: pattern:(?:go)+
.
In the example below we only get the name "John" as a separate member of the results
array:
let str = "Gogo John!";
*!*
// exclude Gogo from capturing
let reg = /(?:go)+ (\w+)/i;
*/!*
let result = str.match(reg);
alert( result.length ); // 2
alert( result[1] ); // John