Moon
Moon

Reputation: 35305

Ruby regex to filter out word ending with a "string" suffix

I am trying to come up with a Ruby Regex that will match the following string:

MAINT: Refactor something
STRY-1: Add something
STRY-2: Update something

But should not match the following:

MAINT: Refactored something
STRY-1: Added something
STRY-2: Updated something

MAINT: Refactoring something
STRY-3: Adding something
STRY-4: Updating something

Basically, the first word after : should not end with either ed or ing

This is what I have currently:

^(MAINT|(STRY|PRB)-\d+):\s([A-Z][a-z]+)\s([a-zA-Z0-9._\-].*)

I have tried [^ed] and [^ing] but they would not work here since I am targeting more than single character.

I am not able to come up with a proper solution to achieve this.

Upvotes: 4

Views: 1565

Answers (3)

halfer
halfer

Reputation: 20469

(Posted on behalf of the question author).

This is what I ended up using:

^(MAINT|(STRY|PRB)-\d+):\s(?:(?!(?:ed|ing)\b)[A-Za-z])+\s([a-zA-Z0-9._\-].*)

Upvotes: 0

Cary Swoveland
Cary Swoveland

Reputation: 110725

r = /
    \A             # match beginning of string
    (?:            # begin a non-capture group
      MAINT        # match 'MAINT'
      |            # or
      STRY\-\d+    # match 'STRY-' followed by one or more digits
    )              # end non-capture group
    :[ ]           # match a colon followed by a space
    [[:alpha:]]+   # match one or more letters
    (?<!           # begin a negative lookbehind
      ed           # match 'ed'
      |            # or
      ing          # match 'ing'
    )              # end negative lookbehind
    [ ]            # match a space
    /x             # free-spacing regex definition mode

   "MAINT: Refactor something".match?(r)   #=> true
   "STRY-1: Add something".match?(r)       #=> true
   "STRY-2: Update something".match?(r)    #=> true

   "MAINT: Refactored something".match?(r) #=> false
   "STRY-1: Added something".match?(r)     #=> false
   "STRY-2: Updated something".match?(r)   #=> false

   "A MAINT: Refactor something".match?(r) #=> false
   "STRY-1A: Add something".match?(r)      #=> false

This regular expression is conventionally written as follows.

r = /\A(?:MAINT|STRY\-\d+): [[:alpha:]]+(?<!ed|ing) /

Expressed this way the two spaces can each be represented a space character. In free-spacing mode, however, all spaces outside character classes are removed, which is why I needed to enclose each space in a character class.

Upvotes: 2

Jan
Jan

Reputation: 43169

You could use

^[-\w]+:\s*(?:(?!(?:ed|ing)\b)\w)+\b.+

See a demo on regex101.com.


Broken down this says:

^                     # start of the line/string
[-\w]+:\s*            # match - and word characters, 1+ then :
(?:                   # non-capturing group
    (?!(?:ed|ing)\b)  # neg. lookahead: no ed or ing followed by a word boundary
    \w                # match a word character
)+\b                  # as long as possible, followed by a boundary
.*                    # match the rest of the string, if any


I have no experience in Ruby but I guess you could alternatively do a split and check if the second word ends with ed or ing. The latter approach might be easier to handle for future programmers/colleagues.

Upvotes: 7

Related Questions