Reputation: 678
I have a regex with a couple of (optional) capture groups. I'm trying to add a new feature that allows a user to add content to one of the capture groups that matches the closing bracket of the regex. I'm struggling to ignore this match
The current regex is /\[(.+?)=(.+?)(?:\|(.+?))?(?:\:(.+?))?\]/g
This allows a user to target data according to:
[key=value|filter:filtervalue]
where filter and filtervalue are optional.
The problem is that for the value it should now be possible to target indexes in an array. For example:
[data=example(products[0].id)]
However, the regex matches only up to .id
so the second capture group is example(products[0
. I would like it to be example(products[0].id)
. I think I should be fine if I can ignore the closing bracket when it is wrapped by parentheses, but I've been unable to figure out how.
Examples that should be matched:
[data=example(products[0].id)]
[data=example(products[index].id)]
[data=regular]
[data=test|number:2]
I created a regex101. Any help is appreciated.
Upvotes: 1
Views: 1592
Reputation: 626689
You may use
/\[([^=\]]+)=((?:\[[^\][]*]|[^\]])+?)(?:\|([^:\]]+):((?:\[[^\][]*]|[^\]])+?))?]/g
See the regex demo
Note that lazy dot matching patterns are replaced with more restrictive negated character classes to make sure there is no overflow from one part of the bracketed string to another. To allow matching 1st-level nested [...]
substrings, a \[[^\][]*]|[^\]]
pattern is used to match [...]
or non-]
substrings is used.
Details:
\[
- a literal [
([^=\]]+)
- Group 1 capturing 1+ chars other than =
and ]
=
- a =
symbols((?:\[[^\][]*]|[^\]])+?)
- Group 2 capturing 1+ occurrences (as few as possible) of:
\[[^\][]*]
- [
, then 0+ chars other than ]
and [
, and then ]
|
- or[^\]]
- any char other than ]
(?:\|([^:\]]+):((?:\[[^\][]*]|[^\]])+?))?
- an optional group matching:
\|
- a |
char([^:\]]+)
- Group 3 capturing 1+ chars other than :
and ]
:
- a colon((?:\[[^\][]*]|[^\]])+?)
- Group 4 capturing the same text as Group 2.]
- a literal ]
char.Upvotes: 1
Reputation: 304
I would probably break it down to two separate regular expressions and "or" them. Below I have used your expression to match the first kind of string:
\[(?:(.+?)=(.+)(?:\|(.+?)?\:(.+?)))\]
[key=value|filter:filtervalue]
And another for the second one:
\[(.+?)=(.+)\]
[data=example(products[0].id)]
Then concatenating them to:
\[(?:(.+?)=(.+)(?:\|(.+?)?\:(.+?))|(.+?)=(.+))\]
Where it first tries to match the tricky part and if that fails, resorts to the more general one.
https://regex101.com/r/d6LwEt/1
Upvotes: 0