Question

By using regex, I am trying to validate a user input as he types.

My pattern is this: "(\\w{3})(\\d{7})(\\w{3})". A valid input can be "XYZ0001112CCC".

I want to validate it as user types. I mean "A", "AB", "ABC", "ABC12", "ABC123", ..., "ABC1234567XY" should also not fail. But "A1", "AB2", "ABCD123", ..., "ABC1234567XY1" must fail. As long as the input doesn't break the rule, I want to assume it as "valid so far". Is this possible with regex?

Was it helpful?

Solution

I would use a combination of "once or not at all" quantifier for the each following part after the first letters, and lookbehinds to validate the previous parts of the input.

For instance:

//                           |first letters (1 to 3)
//                                        | if 3 letters precede...
//                                                         | digits (1 to 7)
//                                                                   | if 7 digits precede...
//                                                                               | 3 letters
Pattern p = Pattern.compile("[a-zA-Z]{1,3}((?<=[a-zA-Z]{3})\\d{1,7})?((?<=\\d{7})[a-zA-Z]{3})?");
String[] inputs = {"XYZ0001112CCC", "A", "AB", "ABC", "ABC12", "ABC123", "A1", "AB2", "ABCD123","ABC1234567XY1"};
Matcher m;
for (String input: inputs) {
    m = p.matcher(input);
    System.out.println("Input: " + input + " --> Matches? " + m.matches());
}

Output:

Input: XYZ0001112CCC --> Matches? true
Input: A --> Matches? true
Input: AB --> Matches? true
Input: ABC --> Matches? true
Input: ABC12 --> Matches? true
Input: ABC123 --> Matches? true
Input: A1 --> Matches? false
Input: AB2 --> Matches? false
Input: ABCD123 --> Matches? false
Input: ABC1234567XY1 --> Matches? false

Note

I've changed your \\w expression to character class [a-zA-Z] because \\w also validates digits. Alternatives to [a-zA-Z] are:

  • \\p{Alpha}
  • [a-z] with Pattern.CASE_INSENSITIVE flag on

Final note

My Pattern takes the last letters as a 3-letter group. If you would also accept 1 or 2 letters, you only need to change the last quantifier expression {3} with {1,3}.

OTHER TIPS

You could change the pattern to simpler one ([a-zA-Z]+)(\d+)([a-zA-Z]+)

Then you could check how many letters was in each group:

Pattern p = Pattern.compile("([a-zA-Z]+)(\d+)([a-zA-Z]+)");
Matcher matcher = p.matcher("ABC1234567XYZ");
if (matcher.find()) {
    String firstLetters = matcher.group(1);
    String digits = matcher.group(2);
    String lastLetters = matcher.group(3);

    //any checks here
}

I recommend you split your validation into two separate regular expressions: one for the valid-so-far validation and one for the final check.

Even then, it's not trivial to validate that e.g. ABC1 is valid so far. A pattern like (\\w{0,3})(\\d{0,7})(\\w{0,3}) won't cut it because X0C would be incorrectly considered valid. I therefore recommend you don't try to solve this problem with regular expressions alone, but with some coded logic.

(It is possible to squeeze all the validation logic into a single regular expression using either lookbehinds or if-then-else conditionals, but I suggest not to do that. It's unreadable and unmaintainable.)

Since the format is rather easy, you could do something like this (I don't know the exact java syntax, but I think the idea is clear):

String input = "DEF3";
String example = "ABC1234567XY";
Pattern regex = Pattern.compile("(\\w{3})(\\d{7})(\\w{3})");

String test_input = input + example.last_digits(example.length-input.length);
Matcher matcher = p.matcher(test_input);
return matcher.find();

This way the only duplication you have is between the example and the regex.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top