119

I'm trying to create a Regex test in JavaScript that will test a string to contain any of these characters:

!$%^&*()_+|~-=`{}[]:";'<>?,./

More Info If You're Interested :)

It's for a pretty cool password change application I'm working on. In case you're interested here's the rest of the code.

I have a table that lists password requirements and as end-users types the new password, it will test an array of Regexes and place a checkmark in the corresponding table row if it... checks out :) I just need to add this one in place of the 4th item in the validation array.

var validate = function(password){
    valid = true;

    var validation = [
        RegExp(/[a-z]/).test(password), RegExp(/[A-Z]/).test(password), RegExp(/\d/).test(password), 
        RegExp(/\W|_/).test(password), !RegExp(/\s/).test(password), !RegExp("12345678").test(password), 
        !RegExp($('#txtUsername').val()).test(password), !RegExp("cisco").test(password), 
        !RegExp(/([a-z]|[0-9])\1\1\1/).test(password), (password.length > 7)
    ]

    $.each(validation, function(i){
        if(this)
            $('.form table tr').eq(i+1).attr('class', 'check');
        else{
            $('.form table tr').eq(i+1).attr('class', '');
            valid = false
        }
    });

    return(valid);

}

Yes, there's also corresponding server-side validation!

pixelbobby
  • 4,368
  • 5
  • 29
  • 49
  • 12
    It's quite funny that the answer to your question lies in the title with the exception of escaping special characters and enclosing forward slashes. – sciritai Dec 02 '11 at 16:46
  • 1
    Why not use `.addClass("check")` and `.removeClass("check")`? And seeing `if (someBoolean == true)` in code always makes me cringe. Just do `if (someBoolean)`. Or, better yet, just do `$(".form table tr").eq(i+1).toggleClass("check", !!this); valid = valid && !!this;`. – gilly3 Dec 02 '11 at 16:46
  • +1 @gill3 thx for the code review- great feedback indeed. I've def used those short-hand methods in the past. – pixelbobby Dec 02 '11 at 17:59
  • @gilly3, it appears to work great in FF but !IE8. love this short-hand. I'm trying to figure out what IE8 is doing differently. – pixelbobby Dec 02 '11 at 18:14

7 Answers7

203

The regular expression for this is really simple. Just use a character class. The hyphen is a special character in character classes, so it needs to be first:

/[-!$%^&*()_+|~=`{}\[\]:";'<>?,.\/]/

You also need to escape the other regular expression metacharacters.

Edit: The hyphen is special because it can be used to represent a range of characters. This same character class can be simplified with ranges to this:

/[$-/:-?{-~!"^_`\[\]]/

There are three ranges. '$' to '/', ':' to '?', and '{' to '~'. the last string of characters can't be represented more simply with a range: !"^_`[].

Use an ACSII table to find ranges for character classes.

Jeff Hillman
  • 7,500
  • 3
  • 31
  • 34
20

Answer

/[\W\S_]/

Explanation

This creates a character class removing the word characters, space characters, and adding back the underscore character (as underscore is a "word" character). All that is left is the special characters. Capital letters represent the negation of their lowercase counterparts.

\W will select all non "word" characters equivalent to [^a-zA-Z0-9_]
\S will select all non "whitespace" characters equivalent to [ \t\n\r\f\v]
_ will select "_" because we negate it when using the \W and need to add it back in

MikeSchem
  • 950
  • 2
  • 16
  • 29
15

The most simple and shortest way to accomplish this:

/[^\p{L}\d\s@#]/u

Explanation

[^...] Match a single character not present in the list below

  • \p{L} => matches any kind of letter from any language

  • \d => matches a digit zero through nine

  • \s => matches any kind of invisible character

  • @# => @ and # characters

Don't forget to pass the u (unicode) flag.

AmirZpr
  • 263
  • 2
  • 9
  • wouldn't you need a ^ to indicate not? – Webber Jan 28 '19 at 16:57
  • 1
    @Webber No. They're in capital and this makes the statement negative. `^` is needed when we use `\w` and `\s` in small letters. – AmirZpr Jan 29 '19 at 10:44
  • 3
    Doesn't this interpret it so that either outside `w` or outside `s`, and since those two don't really intersect it just lets through all of the characters? (Thus not filtering anything.) – Zael Feb 19 '19 at 09:20
  • 2
    @Zael You're right, the regular expression as stated (`/[\W\S]/`) does let everything through. A more accurate representation of what I believe Amir was getting at would be `[^\w\s]`. In the former, the regular expression is saying "match anything that is not alphanumeric **OR** that is not whitespace", which as you mentioned let's everything through since alphanumeric characters are not whitespace and vice versa. The latter says "match anything that is not alphanumeric **AND** that is not whitespace". Of course, exceptions apply in that accented characters (like `À`) are matched by `[^\w\s]`. – Jesse Jan 22 '20 at 07:22
  • this doesn't include the _ char – MikeSchem Sep 01 '20 at 21:33
  • Doesn't work in Firefox, I get "SyntaxError: invalid identity escape in regular expression" . Works fine in Chrome. – Fordy Jun 14 '21 at 09:59
1

A simple way to achieve this is the negative set [^\w\s]. This essentially catches:

  • Anything that is not an alphanumeric character (letters and numbers)
  • Anything that is not a space, tab, or line break (collectively referred to as whitespace)

For some reason [\W\S] does not work the same way, it doesn't do any filtering. A comment by Zael on one of the answers provides something of an explanation.

Harfel Jaquez
  • 289
  • 4
  • 11
  • No, the underscore is missing and all characters out of the ascii range and most of the control characters in the ascii range would match this class. `/[^\w\s]/.test('é') # true`, `/[^\w\s]/.test('_') # false`. – Casimir et Hippolyte Oct 18 '19 at 11:00
0
// The string must contain at least one special character, escaping reserved RegEx characters to avoid conflict
  const hasSpecial = password => {
    const specialReg = new RegExp(
      '^(?=.*[!@#$%^&*"\\[\\]\\{\\}<>/\\(\\)=\\\\\\-_´+`~\\:;,\\.€\\|])',
    );
    return specialReg.test(password);
  };
Arni Gudjonsson
  • 544
  • 9
  • 23
  • Don't use the `RegExp` constructor when you can just use a regex literal. Much less escaping work (most of which are unnecessary anyway), and it's more efficient as well. – Bergi Jul 17 '19 at 21:56
  • Why use a complicated lookahead when you can just match the character directly? – Bergi Jul 17 '19 at 21:58
  • Can you give an example @Bergi? I don't understand what you are suggesting. – Arni Gudjonsson Sep 18 '19 at 17:09
0

to build upon @jeff-hillman answer, this is the complete version

/[\\@#$-/:-?{-~!"^_`\[\]]/

Tests

function noSpecialChars(str) {
  const match = str.match(/[\\@#$-/:-?{-~!"^_`\[\]]/)
  if (!match) return
  
  throw new Error("got unsupported characters: " + match[0])
}

// prettier-ignore
const symbols = ["!", "@", "#", "$", "%", "^", "&", "*", "(", ")", "-", "_", "+", "=", ".", ":", ";", "|","~","`","{","}","[","]","\"","'","<",">","?","/", "\\"]

symbols.forEach((s) => {
  it(`validates no symbol ${s}`, async () => {
    expect(() => {
      noSpecialChars(s)
    }).toThrow();
  })
})
Shining Love Star
  • 5,734
  • 5
  • 39
  • 49
-1

How about (?=\W_)(?=\S).? It checks that the character matched by the . is not a word character (however _ is allowed) and that it's not whitespace.

Note: as @Casimir et Hippolyte pointed out in another comment, this will also match characters like é and such. If you don't expect such characters then this is a working solution.