Regex matching wrong optional group

Viewed 49

I'm trying to match a time format. The time can be written with optional hours, minutes and milliseconds. The only required value is seconds. So all of these times are correctly written and should match.

10:11:12.123 '10 hours, 11 minutes, 12 seconds and 123 milliseconds
10:11:12     '10 hours, 11 minutes and 12 seconds
11:12.123    '11 minutes, 12 seconds and 123 milliseconds
11:12        '11 minutes and 12 seconds
12.123       '12 seconds and 123 milliseconds
12           '12 seconds

My regex looks like this: (?:(?:(\d*):)?(?:(\d*):)?(\d*)(?:\.(\d*))?)

I would like each group to correspond to the right value. So group 1 should always return hours, group 2 should always return minutes, etc... I tried this in Regex101 but as you can see in those results group 1 is always preferred over group 2 when there are no hours in play. It matches the time 11:12 perfectly but it places 11 in group 1 telling me it's hours while it actually is minutes. That's no strange behavior because the minutes in this string matches group 1. But it is not what I want.

How to make sure that each group always corresponds to the right part of the time? Can I prioritize group 2 over group 1 in some way?

1 Answers

You may move the first optional group into the second one so that they were tried in an immediate succession within one greedy matching group:

(?:(?:(\d+):)?(\d+):)?(\d+)(?:\.(\d+))?
   ^^^^^^^^^^^

See the regex demo

Details

  • (?:(?:(\d+):)?(\d+):)? - an optional non-capturing group matching one or zero occurrences of:
    • (?:(\d+):)? - an optional non-capturing group matching and capturing into Group 1 any one or more digits and then just matching :
    • (\d+) - Group 2: one or more digits
    • : - a colon
  • (\d+) - Group 3: one or more digits
  • (?:\.(\d+))? - an optional non-capturing group matching a dot and then capturing into Group 4 one or more digits.
Related