Search code examples
regexregex-groupregex-greedy

Regex Consistent Grouping


I have this rather messy regular expression modified from RegEx for capturing a repeating pattern https://regex101.com/r/Trdwks/1

(([0-9]{1,2}h)[ ]*([0-9]{1,2}min):\s*|([0-9]{1,2}h)():\s*|()([0-9]{1,2}min):\s*)((?:.(?!(\dh\s\d{1,2}min|\dh|\d{1,2}min)))+)

The idea is it matches this string, grouping the hours, minutes, and description.

1h 30min: Title 
- Description Line 1
3h: SECOND TITLE
- Description Line 1
- Description Line 2
- Description Line 3


1h 14min: Title 
- another Great one 42min: Title - Great Movie
- Description Line 2
- Description Line 3

And produces the following the results:

Match 1:
  "1h 30min: Title 
  - Description Line 1"

      Group 1: "1h"
      Group 2: "30min"
      Group 3: "Title 
               - Description Line 1"

Match 2:
  "3h: SECOND TITLE
 - Description Line 1
 - Description Line 2
 - Description Line 3"

      Group 1: "1h"
      Group 2: ""
      Group 3: "SECOND TITLE
               - Description Line 1
               - Description Line 2
               - Description Line 3"

Match 3:
  "1h 14min: Title 
   - another Great one"

      Group 1: "1h"
      Group 2: "14min"
      Group 3: "Title 
                - another Great one"

Match 4:
  "42min: Title - Great Movie
   - Description Line 2
   - Description Line 3"

      Group 1: ""
      Group 2: "42min"
      Group 3: "Title - Great Movie
                - Description Line 2
                - Description Line 3"

I'm having a lot of trouble getting the grouping consistent as the can be only hours, only minutes, or both. So the regex above might put minutes in group 3 or group 6. Is there a way to fix the grouping in the initial or statement to return consistent grouping in each scenario?


Solution

  • This solution just needs to support lookahead assertion.

    (?s)(?=[^:]*\d[^:]*:)(([0-9]{1,2}h)?[ ]*([0-9]{1,2}min)?:\s*)((?:.(?!(\dh\s\d{1,2}min|\dh|\d{1,2}min)))+)

    https://regex101.com/r/gz4r9g/1

    Expanded

     (?s)
     (?= [^:]* \d [^:]* : )
     (                             # (1 start)
          ( [0-9]{1,2} h )?             # (2)
          [ ]* 
          ( [0-9]{1,2} min )?           # (3)
          : \s* 
     )                             # (1 end)
     (                             # (4 start)
          (?:
               . 
               (?!
                    (                             # (5 start)
                         \d h \s \d{1,2} min
                      |  \d h
                      |  \d{1,2} min 
                    )                             # (5 end)
               )
          )+
     )                             # (4 end)
    

    This solution just need to support Branch Reset.

    (?s)(?|([0-9]{1,2}h)[ ]*([0-9]{1,2}min)|([0-9]{1,2}h)()|()([0-9]{1,2}min)):\s*((?:.(?!(\dh\s\d{1,2}min|\dh|\d{1,2}min)))+)

    https://regex101.com/r/pyACdi/1

    Expanded

     (?s)
     (?|
          ( [0-9]{1,2} h )              # (1)
          [ ]* 
          ( [0-9]{1,2} min )            # (2)
       |  ( [0-9]{1,2} h )              # (1)
          ( )                           # (2)
       |  ( )                           # (1)
          ( [0-9]{1,2} min )            # (2)
     )
     : \s* 
     (                             # (3 start)
          (?:
               . 
               (?!
                    (                             # (4 start)
                         \d h \s \d{1,2} min
                      |  \d h
                      |  \d{1,2} min 
                    )                             # (4 end)
               )
          )+
     )                             # (3 end)