How to Ensure The Full Optional Group is Selected

Viewed 36

I have a short code that I am segmenting with the pipe "|" symbol and trying to match with RegEx.

The issue is everything after the 2nd segment is optional, but needs to follow a specific capture group format.

For example the 3rd segment must be a number range such as 5-10. There can be an unlimited number of segments after that that can contain any character except a pipe or a closing bracket.

The following expression works to capture everything I want:

\[tag:( )?(img|image)[ |]{1,3}[^|\]]+([ |]{1,3}[0-9]+-[0-9]+)?([ |]{1,3}[^|\]]*)*]

Unfortunately it also captures tags that are incorrectly formated.

[tag: img | some tag content, I want ] <---- Should Match
[tag: img | some tag content, I want |] <---- Should Not Match

[tag: img | some tag content, I want | 10-30 | optional-option ] <---- Should Match
[tag: img | some tag content, I want | 10-30 || optional-option ] <---- Should Not Match

How can I enforce that an optional group is captured in its entirety?

Bonus question: Why does adding a $ to the end cause nothing to match?

2 Answers

I think your [ |]{1,3} are faulty here, as you want to make sure the | is single and is wrapped with whitespaces. Also, the last [^|\]]* allows zero or more chars other than pipe and bracket, you need to require at least one with the + quantifier.

You can use

\[tag:( )?(img|image)\s*\|\s*[^]|]+(\s*\|\s*[0-9]+-[0-9]+)?(\s*\|\s*[^]|]+)*]

Or, if you want the pipes to be enclosed with single spaces:

\[tag:( )?(img|image) \| [^]|]+( \| [0-9]+-[0-9]+)?( \| [^]|]+)*]

See the regex demo.

Details:

  • \[tag: - [tag: text
  • ( )? - An optional Group 1: a space
  • (img|image) - img or image word
  • | - a | text
  • [^]|]+ - one or more chars other than ] and |
  • ( \| [0-9]+-[0-9]+)? - An optional Group 2: | text, one or more digits, -, one or more digits
  • ( \| [^]|]+)* - zero or more repetitions of the capturing group3: | text, and then one or more chars other than ] and |
  • ] - a ] char.

If in the optional part the third part has to be 10-30 you could make the outer group optional, and inside that group first match the digits-digits part and then make the rest optional.

If you add <---- Should Match at the end of the data, and you add $ at the end of the patter, the pattern does not match anymore as the dollar sign asserts the end of the string.

\[tag: ?(img|image) ?\|[^][|]*( *\| *[0-9]+-[0-9]+( *\|[^][|]+)?)?]
  • \[ Match the opening [
  • tag: ?(img|image) ?\| Match tag: optional space, capture img or image in group 1, optional space and |
  • [^][|]* Match 0+ chars other than [ ] \
  • ( Capture group 2
    • *\| *[0-9]+-[0-9]+ Match optional spaces | optional space and digits-digits
    • ( *\|[^][|]+)*? Optional capture group 3
  • )? Close group 1 and make it optional
  • ] Match the closing ]

Regex demo

Related