Regex for a pattern XXYYZZ

Viewed 627

I want to validate a string which should follow the pattern XXYYZZ where X, Y, Z can be any letter a-z, A-Z or 0-9.

Example of valid strings:

RRFFKK
BB7733
WWDDMM
5599AA

Not valid:

555677
AABBCD

For now I am splitting the string using the regex (?<=(.))(?!\\1) and iterating over the resulting array and checking if each substring has a length of 2.

String str = "AABBEE";
boolean isValid = checkPattern(str);

public static boolean checkPattern(String str) {
   String splited = str.split("(?<=(.))(?!\\1)");
   for (String s : splited) {
       if (s.length() != 2) {
         return false;
       }
   }
   return true;
}

I would like to replace my way of checking with String#matches and get rid of the loop, but can't come up with a valid regex. Can some one help what to put in someRegex in the below snippet?

public static boolean checkPattern(String str) {
    return str.matches(someRegex);
}
5 Answers

You can use

s.matches("(\\p{Alnum})\\1(?!\\1)(\\p{Alnum})\\2(?!\\1|\\2)(\\p{Alnum})\\3")

See the regex demo.

Details

  • \A - start of string (it is implicit in String#matches) - the start of string
  • (\p{Alnum})\1 - an alphanumeric char (captured into Group 1) and an identical char right after
  • (?!\1) - the next char cannot be the same as in Group 1
  • (\p{Alnum})\2 - an alphanumeric char (captured into Group 2) and an identical char right after
  • (?!\1|\2) - the next char cannot be the same as in Group 1 and 2
  • (\p{Alnum})\3 - an alphanumeric char (captured into Group 3) and an identical char right after
  • \z - (implicit in String#matches) - end of string.

RegexPlanet test results:

enter image description here

Since you know a valid pattern will always be six characters long with three pairs of equal characters which are different from each other, a short series of explicit conditions may be simpler than a regex:

public static boolean checkPattern(String str) {
   return str.length() == 6 &&
          str.charAt(0) == str.chatAt(1) &&
          str.charAt(2) == str.chatAt(3) &&
          str.charAt(4) == str.chatAt(5) &&
          str.charAt(0) != str.charAt(2) &&
          str.charAt(0) != str.charAt(4) &&
          str.charAt(2) != str.charAt(4);
}

Would the following work for you?

^(([A-Za-z\d])\2(?!.*\2)){3}$

See the online demo


  • ^ - Start string anchor.
  • (- Open 1st capture group.
    • ( - Open 2nd capture group.
      • [A-Za-z\d] - Any alphanumeric character.
      • ) - Close 2nd capture group.
    • \2 - Match exactly what was just captured.
    • (?!.*\2) - Negative lookahead to make sure the same character is not used elsewhere.
    • ) - Close 1st capture group.
  • {3} - Repeat the above three times.
  • $ - End string anchor.

Well, here's another solution that uses regex and streams in combination.

  • It breaks up the pattern into groups of two characters.
  • keeps the distinct groups.
  • and returns true if the count is 3.
String[] data = { "AABBBB", "AABBCC", "AAAAAA","AABBAA", "ABC", "AAABCC",
        "RRABBCCC" };
String pat = "(?:\\G(.)\\1)+";
Pattern pattern = Pattern.compile(pat);

for (String str : data) {
    Matcher m = pattern.matcher(str);
    boolean isValid = m.results().map(MatchResult::group).distinct().count() == 3;
    System.out.printf("%8s  ->  %s%n",
            str, isValid ? "Valid" : "Not Valid");
}

Prints

  AABBBB  ->  Not Valid
  AABBCC  ->  Valid
  AAAAAA  ->  Not Valid
  AABBAA  ->  Not Valid
     ABC  ->  Not Valid
  AAABCC  ->  Not Valid
RRABBCCC  ->  Not Valid

You can check if a character matches with its following character and also if the count of distinct characters is 3.

Demo:

public class Main {
    public static void main(String[] args) {
        // Test
        System.out.println(isValidPattern("RRFFKK"));
        System.out.println(isValidPattern("BBAABB"));
        System.out.println(isValidPattern("555677"));
    }

    static boolean isValidPattern(String str) {
        return str.length() == 6 &&                
                str.charAt(0) == str.charAt(1) && 
                str.charAt(2) == str.charAt(3) &&
                str.charAt(4) == str.charAt(5) &&
                str.chars().distinct().count() == 3;
    }
}

Output:

true
false
false

Note: String#chars is available since Java-9.

Related