Question

My question is very similar to this one, except that I want to make each alternation optional, and non-repetitive.

Example: The regex needs to match the following strings (this is oversimplified, A and B can be complex):

XAB
XBA
XA
XB
X

It can NOT match XAA, XBB, XABA, XABB, XBAA

Here is what I have so far:

/(X)(?:(A)|(B)){0,2}$/

This allows for repetition (such as XAA), and seems to cause a problem with XB in PHP with an empty array element.

EDIT: Forgot to mention that I need to get the actual values of X, A, and B (if available). It's not just a match of the full string.

Was it helpful?

Solution

Use negative lookahead assertions:

/(X)(?!A{2})(?!B{2})(?:(A)|(B)){0,2}$/

Explanation:

(X)       # Match and capture X
(?!A{2})  # Assert that it's impossible to match AA
(?!B{2})  # Same for BB
(?:       # Non-capturing group:
 (A)      # Match and capture A
|         # or
 (B)      # Match and capture B
){0,2}    # Do this 0-2 times
$         # Then match the end of the string

OTHER TIPS

The cleanest way would be to define each group and then use those definition in a certain order:

# Start defining
(?(DEFINE)
   (?<X>xrules)
   (?<A>arules)
   (?<B>brules)
)
# End defining
^                    # begin of string
(?&X)                # Use rule X
   (?:               # non-capturing group
      (?&A)(?&B)?    # Use rule A and make rule B optional
   |                 # or
      (?&B)(?&A)?    # Use rule B and make rule A optional
   )?                # make it optional
$                    # end of string

Of course, you can change xrules with valid regex like [a-z]+ and brules with [A-Z]+.

Online demo

Your question can have this title: "How to not repeat an optional group?".

$pattern = <<<'LOD'
~
    (?(DEFINE)
        (?<X> FOO )
        (?<A> BAR )
        (?<B> BAZ )
    )

^   \g<X> (?: \g<A> (?! .* \g<A> ) | \g<B> (?! .* \g<B> ) ){0,2}   $
~xs
LOD;

\g<A> (?! .* \g<A> ) forces the group A to be present only once in the string, because of the negative lookahead (i.e: "not followed by anything and group A")

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top