RegEx - Finding multiple dashes in entire string... and a little more

Viewed 1796

So I am HORRIBLE with RegEx... can you help me with the following?

  • only allows 52 total characters
  • only has "a-z", "A-Z", "0-9", & "-" (letters, numbers, and dashes)
  • does not start with "-" (dash)
  • does not end with "-" (dash)
  • does not have "--" (two consecutive dashes together)
  • does not have more than 2 "-" (dashes) in the entire string (this is what I'm having problems with)

So here is a helpful list (I guess):

Pass:
    abc-123
    abc-123-abc
Fail: 
    -abc-123             (fails due to starting with a dash)
    abc-123-             (fails due to ending with a dash)
    abc-123-abc-123      (fails due to 3 dashes)
    abc-12#-abc          (failed due to having a character that is not a-z, A-Z, 0-9, or a dash)

This is what I currently have, but feel free to change it however you would like:

(?!.*--)^[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,50}[a-zA-Z0-9])?$

I'm sure there's a better way to do this, but as mentioned above, I'm horrible with expressions. My expression works, it just doesn't find more than two dashes.

Thanks for your help.

2 Answers

You can use the following PCRE-flavoured regex:

/^(?!.*\-\-)(?!.*\-.+\-.*\-)(?!-)[a-z0-9-]{0,52}(?<!-)$/gmi

Demo

The regex can be made self-documenting by writing it in free-spacing mode:

/
^                    # match beginning of line
(?!.*\-\-.*$)        # the line may not contain two consecutive hyphens
(?!.*\-.*\-.*\-.*$)  # the line may not contain more than two hyphens
(?!-)                # the first char cannot be a hyphen               
[a-z0-9-]{0,52}      # match 0-52 letters, digits and hyphens
(?<!-)               # the last char cannot be a hyphen
$                    # match end of the line
/xgmi                # free-spacing, global, multiline, case indifferent modes

(?!.*\-\-.*$), (?!.*\-.*\-.*\-.*$) and (?!-) are negative lookaheads; (?<!-) is a negative lookbehind.

This matches each line of a string (convenient for showing test cases at the demo). If the string contains a single line the regex can be simplified somewhat:

\A(?!.*\-\-)(?!.*\-.+\-.*\-)(?!-)[a-z0-9-]{0,52}(?<!-)\z

Not that \A and \z are beginning and end of string anchors, whereas ^ and $ are beginning and end of line anchors. Compare the negative lookaheads in this regex with those in the earlier one.

Should it matter, this matches empty strings.

You could use assert a maximum of 52 chars in a positive lookahead.

Then match 1 or more times [a-zA-Z0-9]+ and repeat 0, 1 or 2 times or more times the same pattern preceded with a -

^(?=[a-zA-Z0-9-]{1,52}$)[a-zA-Z0-9]+(?:-[a-zA-Z0-9]+){0,2}$

Explanation

  • ^ Start of line
  • (?= Positive lookahead, assert what is on the right is
    • [a-zA-Z0-9-]{1,52}$ Match any of the listed 1-52 times and assert end of string
  • ) Close lookahead
  • [a-zA-Z0-9]+ Match 1+ times any of the listed to prevent matching an empty string
  • (?: Non capture group
    • -[a-zA-Z0-9]+ Match - and 1+ times any of the listed without the -
  • ){0,2} Close group and repeat 0-2 times
  • $ End of line

Regex demo

Related