Regex Ruby How to group every word within parentheses

Viewed 186

I'm trying to get all the words between the parentheses after a specific word and the end of the string.

For example, I have this case:

p " some other text in downcase LOREM (foo, bar)".scan(/ LOREM \((.*?)\)\z/m)
# [["foo, bar"]]

The regex is getting foo, bar which is between the parenthesis, it's okay, but I'd like to get them like two separate elements within a single array, meaning:

["foo", "bar"]

That's to say, the regex should group every words as a separate element.

My intention is to get everything between LOREM ( and the last closing parenthesis ).

I've tried adding (\b\w+\b), which groups every word in the string. But when adding it to the attempt to get the words from the parenthesis, it returns nothing.

2 Answers

You may use

.scan(/(?:\G(?!\A)\s*,\s*|\sLOREM\s+\()\K\w+(?=[^()]*\)\z)/

See the Ruby demo and the Rubular regex demo. You may replace \w+ with [[:alnum:]]+, or \p{L}+ (to only match letters), or [^\s,()]+ (to match any 1+ chars other than whitespace, ,, ( and )), it all depends on what you want to match inside the paretheses.

Details

  • (?:\G(?!\A)\s*,\s*|\sLOREM\s+\() - either the end of the previous successful match and a , enclosed with 0+ whitespaces, or whitespace, LOREM, 1+ whitespaces and (
  • \K - omit the text matched so far
  • \w+ - consume 1+ word chars
  • (?=[^()]*\)\z) - immediately to the right, there must be 0 or more chars other than ( and ) and then ) at the end of the string.
r = /
    (?<=       # begin a positive lookbehind
      LOREM[ ] # match 'LOREM '
      \(       # match left paren
      |        # or
      ,[ ]     # match a comma followed by a space
    )          # end positive lookbehind
    (?:        # begin a non-capture group
      [^, ")]+ # match one or more characters other than ',', ' ', '"' and ")"
      |        # or
      \"       # match a double quote
      [^, ")]+ # match one or more characters other than ',', ' ', '"' and ")"
      \"       # match a double quote
    )          # end non-capture group
    (?=        # begin a positive lookahead
      .*\)     # match any number of characters followed by a right paren
    )          # end positive lookahead
    /x         # free-spacing regex definition mode

Conventionally this is written

r = /(?<=LOREM \(|, )(?:[^, ")]+|\"[^, ")]+\")(?=.*\))/

Let's try it.

str = "some other text in downcase LOREM (foo, \"bar\", \"baz), daz"

str.scan(r)
  #=> ["foo", "\"bar\""]

The first match, "foo", matches

str.scan /(?<=LOREM \()[^, ")]+/
  #=> ["foo"]

That is, this matches one or more characters other than a comma, space, double quote or left parenthesis, immediately preceded by "LOREM " followed by a left parenthesis.

The next attempted match begins at the end of "foo". There is no match of "L" in "LOREM" so an attempt is made to match ", ", which is met with success. [^, ")]+ does not match "bar", so an attempt is made to match \"[^, ")]+\", which is successful. As ", " is matched within the lookaround it is not part of the match returned. This matches '"bar"'.

\"baz is not matched because it has no closing double quote.

Related