-2

I need an email regular expression as I have hit a hurdle with an input.

<input multiple type="email">

All good unless an email is entered using the following format:

Your Name <name@email.com>

or

 "Your Name" <name@email.com>

Please note that the input has "multiple" which allows for multiple email addresses separated by a comma.

For an email the regular expression in HTML5 browsers is:

/^[a-zA-Z0-9.!#$%&’*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/

This only validates an email address without the name part.

I need a regular expression to allow for a possible the name part as well with quotes or not and even without a name and in the normal email format.

Alan Moore
  • 73,866
  • 12
  • 100
  • 156
Justin Levene
  • 1,630
  • 19
  • 17
  • 1
    Can you tell us what you've tried so far for this requirement? It'd help to know where *specifically* you got stuck. – Jeroen Feb 26 '14 at 13:32

3 Answers3

1

It might not be perfect and could probably be shortened, however since there are no backreferences in JS, I couldn't find a good way to properly handle every cases without the main or (|) condition.

The regex doesn't limit to only two names part, so you can have "My Very Long Name" or My Very Long Name preceeding the email, however it enforces that you must wrap the email within <> if there's a preceeding name. It also supports multiple mails seperated by ,\s*. There's a few places where no multiple spaces are allowed which is between the name parts and right after the whole name and the email.

I also allowed a trailing , at the end, but if you do not want to allow it, you can simply use a lookahead that the following isin't coming (?!\s*$)

^(?:(?:[a-z\d.!#$%&’*+/=?^_`{|}~-]+@[a-z\d-]+(?:\.[a-z\d-]+)*|"?([a-z]| (?! ))+"? <[a-z\d.!#$%&’+/=?^_`{|}~-]+@[a-z\d-]+(?:\.[a-z\d-]+)*>)(?:,\s*|$))*$

RegexPal link:

http://regexpal.com/?flags=gi&regex=^%28%3F%3A%28%3F%3A[a-z\d.!%23%24%25%26%E2%80%99*%2B%2F%3D%3F^_%60{|}~-]%2B%40[a-z\d-]%2B%28%3F%3A.[a-z\d-]%2B%29*|%22%3F%28[a-z]|%20%28%3F!%20%29%29%2B%22%3F%20%3C[a-z\d.!%23%24%25%26%E2%80%99%2B%2F%3D%3F^_%60{|}~-]%2B%40[a-z\d-]%2B%28%3F%3A.[a-z\d-]%2B%29*%3E%29%28%3F%3A%2C\s*|%24%29%29*%24&input=test%40test.com%2C%20%22Foo%20Bar%22%20%3Ctest%40test.com%3E%2C%20Without%20Quotes%20%3Ctest%40test.multiple.domain.parts.com%3E

var mailRx = /^(?:(?:[a-z\d.!#$%&’*+/=?^_`{|}~-]+@[a-z\d-]+(?:\.[a-z\d-]+)*|"?([a-z]| (?! ))+"? <[a-z\d.!#$%&’+/=?^_`{|}~-]+@[a-z\d-]+(?:\.[a-z\d-]+)*>)(?:,\s*|$))*$/i;

mailRx.test('test@test.com'); //true
mailRx.test('"Foo Bar" <test@test.com>'); //true
mailRx.test('Without Quotes <test@test.com>'); //true
mailRx.test('test@with.multiple.mail, "Yet Another" <one@to.test>'); //true
mailRx.test('"Missing Smaller Sign" one@to.test>'); //false
mailRx.test('missing@comma.testone@to.test'); //false
plalx
  • 42,889
  • 6
  • 74
  • 90
  • Seems to works like a charm, however pattern is not overriding the type="email" in input which is a shame. – Justin Levene Feb 26 '14 at 16:36
  • Found a bug. Does not allow a single email, it must have more than one email. – Justin Levene Feb 26 '14 at 16:44
  • @JustinLevene I guess you can just use `type="text"` with pattern even though you lose some sementics meaning. There must be ways to override the default behaviour as well, but I haven't played much with HTML5 yet (still had to work in IE8 at work hehe...). However for the other issues I am not sure what you mean. I have tried and it does allow a single email or multiple ones. See the edited post answer. – plalx Feb 26 '14 at 19:14
  • @JustinLevene I also fixed an issue where multiple domain parts were not recognized. If you whish to capture specific parts, like the name, you can simply add capturing groups around these. – plalx Feb 26 '14 at 19:29
0

This needs testing, but perhaps something in this direction:

// Regex extended to include possible "<", ">", and "," at end
var re = /^<?[a-zA-Z0-9.!#$%&’*+\/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*>?,?$/,
    name = [],                  // Temporary placeholder for name
    rec  = [],                  // Records
    inp  = email.split(/[ ]+/)  // Split input on space(s)
;

for (i = 0; i < inp.length; ++i) {
    if (re.test(inp[i])) { // Check if valid email
        rec.push({
            name: name.join(' '),               // Name
            email: inp[i].replace(/[<>,]/g, '') // Remove <, > and ,
        });
        name = [];
    } else {
        name.push(inp[i])   // Not valid e-mail, add to name array.
    }
}

Sample fiddle with modifications

You could skip the creation of result array, but then, why do the same job on the server? As anything not an e-mail would be pushed as a name the only fail criteria are if name has a length greater then 0 at end of loop, or the records array is empty.

The regexp needs work. But concept could be a start. It would for example validate:

Some name <some@example.net
                           |
                           +--- missing >

etc. Though that one in particular could simply be a nicety if you do not want to be strict.

As there are no validation on name, anything not validating as e-mail, would be a name. If you need to validate name as well you could have a look at this or similar:


An alternative to validating the name is to remove unwanted characters, at least in the ASCII range. For example:

  re_name = /^'|[\x00-\x1f\x21-\x26\x28-\x2b\x2f-\x40\x5b-\x5f\x7b-\x7e]/g

  ' At start of name
  0x00 - 0x1f NUL -> US
  0x21 - 0x26 ! -> &
  0x28 - 0x2b ( -> +
  0x2f - 0x40 / -> @
  0x5b - 0x5f [ -> _
  0x7b - 0x7e { -> ~

And then do:

  name : name.join(' ').replace(re_name, ''),

Sample input:

var email = 
    'LastnameA, FirstnameA <nameA@example.net>, ' + 
    'FirstnameB LastnameB <nameB@example.net, ' + // missing >
    'LastnameC <nameC@example.net>, ' + 
    'LastD NameD, MiddleD NameD, FirstD NameD nameD@example.net ' +
    '"FirstE E. LastnameE" <nameE@example.net>, ' + 
    'nameF@nonamebeforeme.example.net ' +
    'Połącz Słońce w Mózu <plz@example.net> ' +
    'L33t Spiiker /good/mofo]] <l33t@hotmail.com> ' +
    'Moot @m Email'
;

Output:

8 records => [
    {
        "name": "LastnameA, FirstnameA",
        "email": "nameA@example.net"
    },
    {
        "name": "FirstnameB LastnameB",
        "email": "nameB@example.net"
    },
    {
        "name": "LastnameC",
        "email": "nameC@example.net"
    },
    {
        "name": "LastD NameD, MiddleD NameD, FirstD NameD",
        "email": "nameD@example.net"
    },
    {
        "name": "FirstE E. LastnameE",
        "email": "nameE@example.net"
    },
    {
        "name": "",
        "email": "nameF@nonamebeforeme.example.net"
    },
    {
        "name": "Połącz Słońce w Mózu",
        "email": "plz@example.net"
    },
    {
        "name": "Lt Spiiker goodmofo",
        "email": "l33t@hotmail.com"
    }
]
Leftovers: "Moot @m Email"
Community
  • 1
  • 1
user13500
  • 3,817
  • 2
  • 26
  • 33
0

combine 2 regular expressions. Username:

/^[a-z][a-z0-9_\.]{0,24}$/i

And email which you already has:

/^[a-zA-Z0-9.!#$%&’*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/

after that implement logic for your needs

r.r
  • 7,023
  • 28
  • 87
  • 129
  • The username regex will produce many false negative ie `François` `O'Neil` `Müller` `«»© ↓äßë‘’ðüïŀïŀ` ... – Toto Feb 26 '14 at 13:54