1

I am new to regex, i am trying to validate a field the user is entering weekend days in a comma separated format and using 2 characters abbreviation for the day. i developed the following pattern that is not working as i want it to:

^(fr|sa|su|mo|tu|we|th)?(?(1)(,fr|,sa|,su|,mo|,tu|,we|,th)){0,5}$

This pattern successfully matches the desired input like the following:

fr

mo

fr,sa

fr,tu

su,mo,tu,we,fr,sa

but it also matches the following wrong enteries:

fr,fr,fr,fr

sa,sa,sa,sa

I want a way to force the second group to repeat only different values included in the OR construct. is there a way regex can do that?

T.G
  • 53
  • 5
  • Idea: when your regex turns so complicated that you can't write it down yourself, maybe consider not using a single regex instead. – GhostCat Jul 08 '18 at 18:00
  • Normally, you'd use conditionals to keep from matching the same day again. This way you get exactly what days did match. And you don't need to worry about the order they are listed. _Unfortunately_ Java doesn't do _conditionals_. If you can use an engine that can do conditionals, use this regex on it `^(?:(?:^|,)(?:(?(1)(?!))(fr)|(?(2)(?!))(sa)|(?(3)(?!))(su)|(?(4)(?!))(mo)|(?(5)(?!))|(tu)(?(6)(?!))|(we)|(?(7)(?!))(th))){1,5}$` See https://regex101.com/r/zgdBKF/1 and possible 3rd party worki around https://stackoverflow.com/questions/3687921/conditional-regular-expression-in-java –  Jul 08 '18 at 20:44

1 Answers1

1

You may use a (?!.*([a-z]{2}).*\1) negative lookahead at the start of regex to disallow repeating 2-letter values in the string (due to the string format, you do not even need word boundaries or comma context checks in the lookahead):

^(?!.*\b([a-z]{2})\b.*\b\1\b)(fr|sa|su|mo|tu|we|th)?(?:,(?:fr|sa|su|mo|tu|we|th)){0,5}$

See the regex demo.

The (?!.*\b([a-z]{2})\b.*\b\1\b) is a negative lookahead that fails the match if there is a duplicate two-letter chunk in the string.

Java demo:

String day = "fr|sa|su|mo|tu|we|th";
String pattern = "(?!.*\\b([a-z]{2})\\b.*\\b\\1\\b)(?:" + day + ")?(?:,(?:" + day + ")){0,5}";
if (s.matches(pattern)) {
    System.out.println("Valid!");
} else {
    System.out.println("Invalid!");
}

Note that String#matches requires a full string match, so ^ and $ are not required.

Note you may shorten the day part using character classes, fr|sa|su|mo|tu|we|th => fr|s[au]|mo|t[uh]|we.

Wiktor Stribiżew
  • 607,720
  • 39
  • 448
  • 563