68

I'm having a tough time getting this to work. I have a string like:

something/([0-9])/([a-z])

And I need regex or a method of getting each match between the parentheses and return an array of matches like:

[
  [0-9],
  [a-z]
]

The regex I'm using is /\((.+)\)/ which does seem to match the right thing if there is only one set of parenthesis.

How can I get an array like above using any RegExp method in JavaScript? I need to return just that array because the returned items in the array will be looped through to create a URL routing scheme.

Oscar Godson
  • 31,662
  • 41
  • 121
  • 201
  • 2
    When you say "one set of parentheses", are you referring to *nested* parentheses? It's basically beyond the power of regular expressions to understand the whole "balanced parentheses" thing. – Pointy Jun 01 '11 at 22:15
  • 1
    _Anything_ inside of the `()`. So if the string was `something/([0-9])/((a)(b))` it'd return `[ [0-9], (a)(b) ]`. Im not going to validate these, just throwing em inside a `new RegExp()` – Oscar Godson Jun 01 '11 at 22:20

4 Answers4

151

You need to make your regex pattern 'non-greedy' by adding a ? after the .+

By default, * and + are greedy in that they will match as long a string of chars as possible, ignoring any matches that might occur within the string.

Non-greedy makes the pattern only match the shortest possible match.

See Watch Out for The Greediness! for a better explanation.

Or alternately, change your regex to

\(([^\)]+)\)

which will match any grouping of parentheses that do not, themselves, contain parentheses.

Audwin Oyong
  • 2,247
  • 3
  • 15
  • 32
Rob Raisch
  • 17,040
  • 4
  • 48
  • 58
  • Perfect, that matches it! but im stuck at how to get make an array from that? I just get `["([0-9])"]` with `str.match(/\([^\)]+\)/g)` :( – Oscar Godson Jun 01 '11 at 22:27
  • 2
    You're missing the internal start and end capture parens. Try /\\(([^)]+)\\)/g which will capture all possible parenthesized values. Note the *second* pair of parens after \\( and before \\). – Rob Raisch Jun 01 '11 at 22:29
  • Thanks for the "greedy" part. Helped a lot. – Dmitri Dec 08 '15 at 11:18
  • 2
    In my case to include parens that do not contain any other parens, I had to add open parens to the regex: `\(([^\)(]+)\)` – Aryan Firouzian Jan 28 '16 at 08:55
  • This is great, but is there a way to not match the actual parens themselves? I can always remove the first and last character of every match, but it would be nice if it could all be done in one line. – adrianmcli Jan 17 '17 at 21:48
  • The simplest way would be to do a replace and take the result, as in: `result=str.replace(/^.+?\(([^\)]+)\).+$/,'$1')` which will replace a string that contains a parenthesized span of text with the text itself. See https://jsfiddle.net/b3rs34d4/ – Rob Raisch Jan 17 '17 at 23:23
  • And here's a one-liner that extracts all parenthesized values from a multi-lined string... https://jsfiddle.net/pv8pzL6j/ – Rob Raisch Jan 17 '17 at 23:39
  • And just for the record, for complex extractions I've found regexen to be of limited value due to their complexity and lack of easy readability. Adding multi-line, free-whitespaced and commented sugar like [XRegExp](http://xregexp.com/) helps a lot, but I've found writing a job-specific parser using [PegJS](https://pegjs.org/) to be invaluable. I'm happy to explain more if anyone's interested. – Rob Raisch Jan 17 '17 at 23:45
16

Use this expression:

/\(([^()]+)\)/g

e.g:

function()
{
    var mts = "something/([0-9])/([a-z])".match(/\(([^()]+)\)/g );
    alert(mts[0]);
    alert(mts[1]);
}
Chandu
  • 81,493
  • 19
  • 133
  • 134
6

If s is your string:

s.replace(/^[^(]*\(/, "") // trim everything before first parenthesis
 .replace(/\)[^(]*$/, "") // trim everything after last parenthesis
 .split(/\)[^(]*\(/);      // split between parenthesis
arturh
  • 449
  • 5
  • 9
4
var getMatchingGroups = function(s) {
  var r=/\((.*?)\)/g, a=[], m;
  while (m = r.exec(s)) {
    a.push(m[1]);
  }
  return a;
};

getMatchingGroups("something/([0-9])/([a-z])"); // => ["[0-9]", "[a-z]"]
maerics
  • 151,642
  • 46
  • 269
  • 291