Radex
Radex

Reputation: 8587

Regular expression /[a-zA-Z0-9_-]/ return true when passing `!!!`

I have the following regular expression /[a-zA-Z0-9_-]/ and I need to return false when the string contains only characters from a to z lower and uppercase, numbers, and _ and -.

//var str = 'EKyirtVHsK0'; ok
var str = '!%!irtVHsK0'; // should return false
var result = /[a-zA-Z0-9_-]/.test(str);
alert(result);

Upvotes: 2

Views: 14943

Answers (4)

dodov
dodov

Reputation: 5844

You could check if a character other than the specified ones is found and return true if it does:

console.log(isValid("!%!irtVHsK0"));
console.log(isValid("char09_-"));

function isValid(str) {
  return str.match(/[^a-zA-Z0-9_-]/) !== null;
}

This [...] matches specific characters.
This [^...] excludes specific characters.

In this case, /[^a-zA-Z0-9_-]/ matches a character that is not a-z, A-Z, 0-9, _ or -.

The match() method will return the first occurrence of the specified pattern or all of them, if the global flag is enabled. If nothing is found, it returns null.

str.match(/[^a-zA-Z0-9_-]/g); // "g" enables the global flag

Please note, the ^ hat symbol has different meaning when it's not inside square brackets like [^...]. If it's outside, it indicates the start of a string.

I would recommend visiting this page to learn regex, it's very easy. You could also use this to test your regex.

Upvotes: 3

Nicolas Straub
Nicolas Straub

Reputation: 3411

var str = 'EKyirtVHsK0'; // ok
var str2 = '!%!irtVHsK0'; // should return false
var result = /^[a-z0-9_-]+$/i.test(str);
var result2 = /^[a-z0-9_-]+$/i.test(str2);
alert(result);
alert(result2);

As stated by Wiktor Stribizew, you are doing partial matches with your regular expression. in this particular case it means the string will be traversed character by character until it finds a match or it reaches the end of the string. With your string it will match at index 3 (the i), and return true. Using ^ at the beginning and $ at the end of the expression, you force it to match the contents of the entire string exactly, since ^ matches the beginning of a string and $ matches the end.

/^[a-zA-Z0-9_-]$/ matches a string consisting of any single character from a to z, A to Z, 0 to 9, a hyphen or an underscore, so it would match '3', 'b', 'E', '-' or '_' but not 'ab' or 'er', because the ^ and $ effectively restrict the string to just one character in this case.

To test for a string consisting of any amount of those characters in particular, you have to introduce an asterisk (if the string can be empty) or a plus sign (if it must contain at least one character). So /^[a-zA-Z0-9_-]+$/ returns true only if the string is comprised exclusively of the character ranges in the expression.

You can also do away with the A-Z portion using the i flag, which tells the expression to do case-insensitive matches, leaving you with /^[a-z0-9_-]+$/i as the final expression.

Upvotes: -1

axiac
axiac

Reputation: 72226

There is nothing wrong with your code. Only your expectations are a bit off.

The documentation of RegExp.test() mentions:

Use test() whenever you want to know whether a pattern is found in a string (similar to the String.prototype.search() method [...])

The test() method doesn't verify if your entire string matches the regular expression but only if there is a substring of it that matches.

In its current form, your code checks if the string contains at least one character from the range (letters, digits, minus or dash).

If you need to check the matching against the entire string you should use boundaries (and probably a quantifier):

/^[a-zA-Z0-9_-]+$/
  • ^ matches the beginning of the string;
  • $ matches the end of the string;
  • + means the previous expression matches once or more; the previous expression here is the [...] range.

This updated expression matches the strings that contain only letters, digits, dashes and minuses and are not empty; use * instead of + to allow empty strings; * makes the previous expression match zero or more times.


Update:

A better way is to put ^ as the first character of the range and interpret the value returned by test() the other way around. ^ negates the meaning of the range block ([...]). [^a-zA-Z0-9_-] matches any character that is not a letter, a digit, dash or comma.

I need to return false when the string contains only characters from a to z lower and uppercase, numbers, and _ and -.

A string that matches the regexp /[^a-zA-Z0-9_-]/ is valid for your needs (it contains at least one character that is not a letter, a digit, dash or comma).

var re = /[^a-zA-Z0-9_-]/
alert('Valid: ' + re.test('EKyirtVHsK0'));               // false
alert('Valid: ' + re.test('!%!irtVHsK0'));               // true

Upvotes: 6

Wiktor Stribiżew
Wiktor Stribiżew

Reputation: 626845

Your string contains symbols that are matched with your regex that finds partial matches. In fact, if you use str.match(/[a-zA-Z0-9_-]/), you will see you matched i.

Add anchors and a quantifier:

/^[a-zA-Z0-9_-]*$/
 ^             ^^

Or, if 1+ chars are required in the input:

/^[a-zA-Z0-9_-]+$/
               ^

Upvotes: 7

Related Questions