Home > Software engineering >  Matching hour-minute-second (hms) duration string
Matching hour-minute-second (hms) duration string

Time:04-27

I want to match the digits before h, m, and s into their respective capture groups. I want to match all 3 groups if possible. If one or two of the other groups are missing then match the last group(s).

As seen in the image below and in this regex and string

But I want 7 matches. Where the first match contains 3 groups, the 2:nd to 4:th match contain 2 groups each and the last 3 matches contain 1 group each.

So I want:

  1. Match 1: 11h22m33s
    1. Group 1: 11
    2. Group 2: 22
    3. Group 3: 33
  2. Match 2: 11h22m
    1. Group 1: 11
    2. Group 2: 22
  3. Match 3: 11h33s
    1. Group 1: 11
    2. Group 2: 33
  4. Match 4: 22m33s
    1. Group 1: 22
    2. Group 2: 33
  5. Match 5: 11h
    1. Group 1: 11
  6. Match 6: 22m
    1. Group 1: 22
  7. Match 7: 33s
    1. Group 1: 33

Edit

The test strings can be contained within other strings! E.g. 08:00 11h. See https://regex101.com/r/RWA9Oy/1

CodePudding user response:

If you need to make sure the whole string matches the pattern, you can use

^(?!$)(?:(\d )h)?(?:(\d )m)?(?:(\d )s)?$

See the regex demo.

If you need to extract these strings from the longer texts, you can use

\b(?=\w)(?:(\d )h)?(?:(\d )m)?(?:(\d )s)?\b(?!\w)

See this regex demo.

Details:

  • ^ - start of string
  • (?!$) - the string can't end at the very start (=cannot be empty)
  • \b(?=\w) - a word boundary where the char immediately on the right is a word char (=is a letter, digit or _)
  • (?:(\d )h)? - an optional non-capturing group matching one or more digits (capturing them into Group 1) and a h letter
  • (?:(\d )m)? - an optional non-capturing group matching one or more digits (capturing them into Group 2) and a m letter
  • (?:(\d )s)? - an optional non-capturing group matching one or more digits (capturing them into Group 3) and a s letter
  • $ - end of string
  • \b(?!\w) - a word boundary where the char immediately on the right is NOT a word char or at the end of string.

Just in case you want allow any amount of whitespace between the components, you may add \s*:

^(?!\s*$)\s*(?:(\d )\s*h\s*)?(?:(\d )\s*m\s*)?(?:(\d )\s*s)?\s*$

Or, for partial matching

\b(?=\w)(?:(\d )\s*h\s*)?(?:(\d )\s*m\s*)?(?:(\d )\s*s)?\b(?!\w)

See this regex demo and another regex demo.

  • Related