Regex unexpected value match

In string validation exercise credit card number should be number and 16 digit long
so I tried

Regex1

bool validateCreditCardNumber(String number) {
  final numbersOnly = RegExp(r'^[0-9]+$');
  final validLength = RegExp(r'.{16}');

  return numbersOnly.hasMatch(number) && validLength.hasMatch(number);
}

  print(validateCreditCardNumber('123r123412341234')); //false
  print(validateCreditCardNumber('1234123412341234')); //true
  print(validateCreditCardNumber('123412341234123')); //false
  print(validateCreditCardNumber('12341234123412345')); //false

&

Regex2

 final regex = RegExp(r'[0-9]{16}');
  
  print(regex.hasMatch('123r123412341234')); //false
  print(regex.hasMatch('1234123412341234')); //true
  print(regex.hasMatch('123412341234123')); //false
  print(regex.hasMatch('12341234123412345')); //false

in both Regex last one was expected as false as it contains 17 numbers but results on both Regex are

false
true
false
true

I have also check challenge answer on github answer are same, regex is validating 17 numbers, What am I doing wrong?
How to validate only 16 numbers?

2 Likes

The point is that we use hasMatch method which literally just checks do you have match or not. Or, in other words, does the whole target text has a sequence of symbols that corresponds to the regexp.

Regex1

The first regexp (numbersOnly) ^[0-9]+$ definitely has match for the last test line with 17 numbers 12341234123412345 — all this line contains only digits from start to end — so no surprise that this regexp gives us true.

The second regexp (validLength) .{16} also has a match — our test line 12341234123412345 has 16 + 1 = 17 digits — so this regexp also outputs true (see #1 on the screenshot below).

Regex2

The expression from the second example ([0-9]{16}) acts similarly — it says that the test line 12341234123412345 do has 16 digits: this 16 digits digit sequence is included to 17 digits line — and that regexp also says Yes, I have a match! (see #2 on the screenshot below).

How to validate only 16 numbers?

Just add the start of line and end of line tokens to what you have:
^[0-9]{16}$
(or even shorter ^\d{16}$)

See #3 one the screenshot:

Hope you get the idea.

3 Likes

Hi Take this as an example:

  final regex = RegExp(r'[0-9]{3}');
  print(regex.hasMatch('111122223')); // true

It is the same asking the string '111122223' contains the substring with exactly 3 digits. The answer is certainly true.

Therefore, I think that the answer provided for the credit card validation exercise in GitHub is wrong.

As said by @eshfiled , you have to specify start token: ^ and end token: $ in the Regex pattern if you want to match exact number of digit, i.e.

  final regex = RegExp(r'^[0-9]{3}$');
  print(regex.hasMatch('111122223')); // false
 print(regex.hasMatch('123')); // true
3 Likes

@ellery Thanks for help!

Therefore, I think that the answer provided for the credit card validation exercise in GitHub is wrong.

I have created a pull request to fix it — hope maintainer will approve it soon.

@shakirkasmani92

This is question was for Regex for Dart

Regular expressions are self-contained stand-alone mini-language and particularly with these simple cases we are talking about it works absolutely the same in any programming language supporting regexps.

Still getting true for invalid value

Check this:

void main() {
  final badRegex = RegExp(r'[0-9]{16}');
  print(badRegex.hasMatch('123r123412341234')); // false [OK]
  print(badRegex.hasMatch('1234123412341234')); // true [OK]
  print(badRegex.hasMatch('123412341234123')); // false [OK]
  print(badRegex.hasMatch('12341234123412345')); // true [NOT OK!!!]

  final goodRegex = RegExp(r'^[0-9]{16}$');
  print(goodRegex.hasMatch('123r123412341234')); // false [OK]
  print(goodRegex.hasMatch('1234123412341234')); // true [OK]
  print(goodRegex.hasMatch('123412341234123')); // false [OK]
  print(goodRegex.hasMatch('12341234123412345')); // false [OK]
}

3 Likes

Sorry there was issue in my test case, RegExp(r'^[0-9]{16}$') is correct.

Thanks for the help

1 Like

@shakirkasmani92 Good question.
@eshfield and @ellery Thank you for your help.

I wish I could say this was just a typo, but when I was creating the exercise question, I completely missed the logic of an explicit range regex also matching a longer string because of its substrings. So, yes, adding ^ and $ to match the entire string is a good solution. One benefit of being a writer is having people like you catch my errors. I get to learn, too. Thank you.

I’ve merged the PR in the dabb-materials repo: Update the credit card regular expression by eshfield · Pull Request #2 · kodecocodes/dabb-materials · GitHub

This error also affect the text of Chapter 1, “Strings”, so I’ve updated the internal repo to fix that. The online book should reflect that change soon. The new content states the following. Bolded text is new:

final goodLength = RegExp(r'^.{12,}$');
if (!password.contains(goodLength)) {
  print('Your password must be at least 12 characters long!');
}

Recall that ^ and $ match the beginning and end of the string. This ensures you’re validating the whole password. The {} curly braces indicate a length range in regex. Using {12} means a length of exactly 12, {12,15} means a length of 12 to 15 characters, and {12,} means a length of at least 12 with no upper limit. Because {12,} follows the . character, you’re allowing 12 or more of any character.

While the original book example still worked in practice, the explanation about {12} and {12,15} would not have worked for the upper limits.

4 Likes