I need a regular expression that will accept well-formed emails in several formats (see below) that will be input in a comma-separated list. I have the basic email address
validation regex,
^[\w\d._%+-]+#(?:[\w\d-]+\.)+(\w{2,})(,|$)
which can handle test cases A and B, but not the others. I also tried
^(\<)?[\w\d._%+-]+#(?:[\w\d-]+\.)+(\w{2,})(\>)?(,|$)
which was able to handle A, B, and C, but only validated the first email address in each of test cases D and E. I haven't even gotten to testing a regex for format 3.
tl;dr Need a regex that will validate email addresses 1, 2, and 3.
Good website to test your regular expressions: Online Javascript Regex Tester
Data
Test Cases
A. nora#example.com
B. nora#example.com, fred#example.com
C. <nora#example.com>, fred#example.com
D. <nora#example.com>, <fred#example.com>
E. fred#example.com, <nora#example.com>
Email Address Formats
1. xyz#example.com
2. <xyz#example.com>
3. "xyz"<xyz#example.com>
EDIT
I flagged this as a possible duplicate of:
Validate email address in JavaScript?
which, in turn, seems to be a duplicate of:
Using a regular expression to validate an email address
both of which contain much discussion on the validity of regex as email validation. However, the top-voted regexes provided don't seem to do quite what I want so I don't consider this answered yet.
None of the links or answers provided was the best answer for this question. Here's what solved it:
/*
* regex checks: must start with the beginning of a word or a left caret
* must end with either the end of a word or a right caret
* can handle example.example.com as possible domain
* email username can have + - _ and .
* not case sensitive
*/
var EMAIL_REGEX = /(\<|^)[\w\d._%+-]+#(?:[\w\d-]+\.)+(\w{2,})(\>|$)/i;
var emails = emailList.trim().split(',');
var validEmails = [];
var invalidEmails = [];
for (var i = 0; i < emails.length; i++) {
var current = emails[i].trim();
if(current !== "") {
//if something matching the regex can be found in the string
if(current.search(EMAIL_REGEX) !== -1) {
//check if it has either a front or back bracket
if(current.indexOf("<") > -1 || current.indexOf(">") > -1) {
//if it has both, find the email address in the string
if(current.indexOf("<") > -1 && current.indexOf(">") > -1) {
current = current.substr(current.indexOf("<")+1, current.indexOf(">")-current.indexOf("<") -1);
}
}
}
if(EMAIL_REGEX.test(current)) {
validEmails.push(current);
} else {
invalidEmails.push(current);
}
}
}
It would be simpler to first split the comma-separated list into an array, and validate each member of the array individually. That would make the regex easier to write (and read and maintain), and also give you the ability to provide specific feedback to the user who entered the list ("the 3rd email address is invalid").
So assuming you did that with a split
var bits = csv.split(',');
Iterate through the bits array
for (var i = 0; i < bits.length; ++i) {
if (!validateEmail(bits[i])) {
alert("Email #" + (i+1) + " is bogus");
}
}
Then for the regex, something like this will capture 2 and 3
(\"[a-z0-9\s]+\"\s+)?\<[\w\d._%+-]+#(?:[\w\d-]+\.)+(\w{2,})\>
And you can use the simpler one to capture simple email addresses without the < or the name in quotes in front of it.
A single regex will not necessarily run any faster than two if tests, especially if you short-circuit the or by putting the more likely one first. It's also harder to read and maintain. Lastly it's extra tricky because you need a lookahead: the final > is only ok if the string ahead of the email address includes a < right before the first character of the email.
So my $0.02 = not worth it. Just do two regexes.
This validateEmail function will check for the basic syntax of an email address (xyz#example.com).
The included ifs will check for the alternate formatting (<xyz#example.com>, 'xyz' <xyz#example.com>) and only validate the actual email portion.
Items with only < or > are deemed invalid for poor formatting (Nope#example.com>), same with any emails lacking the basic structure required (invalidExample.com).
var emailList = "abc#example.com,<lmn#example.com>,'xyz' <xyz#example.com>,invalidExample.com,Nope#example.com>,'Still93e-=48%5922=2 Good' <xyz#example.com>";
var emails = emailList.split(",");
//Loop through the array of emails
for (var i = 0; i < emails.length; i++) {
var isValid = 1;
var cur = emails[i];
// If it has a < or a >,
if( cur.indexOf("<") > -1 || cur.indexOf(">") > -1 ){
// Set it invalid
isValid = 0;
// But if it has both < and >
if( cur.indexOf("<") > -1 && cur.indexOf(">") > -1 ){
//Set it valid and set the cur email to the content between < and >
isValid = 1;
cur = cur.substr(cur.indexOf("<")+1, ( cur.indexOf(">") - cur.indexOf("<") - 1 ));
}
}
//Run the validate function
if ( !validateEmail(cur) )
isValid = 0;
// Output your results. valid = 1, not valid = 0
alert("Orig: "+emails[i] +"\nStripped: "+cur+"\nIs Valid: "+isValid);
}
function validateEmail(curEmail){
var emailValid = /.*\#.*\..*$/g;
return (curEmail.test(emailValid));
}
jsFiddle
Will something like this help?
I have tested 2. and 3., and it detects both the patterns.
var isEmail_re = /^\s*[\w\-\+_]+(\.[\w\-\+_]+)*\#[\w\-\+_]+\.[\w\-\+_]+(\.[\w\-\+_]+)*\s*$/;
function isEmail (s) {
return String(s).search (isEmail_re) != -1;
}
alert(isEmail ('"xyz"<xyz#example.com>'));
http://jsfiddle.net/epinapala/BfKrR/
Related
I'm working on a project that has an input field requiring user to enter only any of the following three options:
Number like 150
Number starting with one letter (which must be N, not case sensitive) like N150
Number ending with one letter (which must be N, not case sensitive) like 150N
Any other value like:
150x will return error message wrong input
x150 will return wrong input
1N50 will return wrong position
The correct way to do this is to make an array of valid numbers and then to check if the given text exists on your array.For example:
var validNumbers = [ 150, N150, 150N ];
if (validNumbers.indexOf(parseInt(num, 10)) >=0 ) {
//Match
}
You'll need an indexOf function for IE:
if (!Array.prototype.indexOf) {
Array.prototype.indexOf = function(needle) {
for(var i = 0; i < this.length; i++) {
if(this[i] === needle) {
return i;
}
}
return -1;
};
}
check this answer :
Regular expression to match a number range in JavaScript
and adjust to your needs, you can easily add the "N" at the end or at the beginning by adding regex part to make accept values like :
-N150
-150N
-130N
-N130
A non regexapproach (just to show why regex is useful):
function test(value){
//convert to array
value=value.split("");
//check the number
function isnumber(num){
return num.every(n=>"1234567890".includes(n));
}
//weve got three possibilities:
//150
if(isnumber(value)) return true;
//N150
var [n,...rest]=value;
if(n==="N" && isnumber(rest)) return true;
//150N
var n=value.pop();
return n==="N" && isnumber(value);
}
http://jsbin.com/kafivecedi/edit?console
Tl;dr: I need to be able to compare two string of varying length, and pick out matches between them that are greater than 3 characters. As any matches greater than 3 characters are most likely full words. I'm working with angularJs, jQuery, and TypeScript if that's helpful to anyone.
I need to be able to compare two strings, and note matches over 3 characters in length. This would be done on names, addresses, and email addresses.
Basically the way the page works in this part of the web app is, there is a user who is suspected to be a duplicate account of another user. The suspected duplicate is flagged, and the web app user looks at the person flagged, and a list of people that may be the original account.
Parts of the flagged user, and potential matches have their name, address, and email highlighted on words that match. To guide the web app user, so they can better see at a glance why specific people were pulled.
For example if the flagged user had an email titled
JohnDoe1234#gmail.com
And potential match number one, of maybe 3, had an email titled
JohnathanDoe#yahoo.com
Then the words John, and Doe, should be highlighted, so the person deciding whether a flagged user is a dudplicate account or not can see at a glance that the flagged user simply created a new email with a shortened version of their first name. The emails closely match, the first name, and last name match, and the addresses are the same, they're a duplicate, deny their account.
Undertaking this task, this is what I had written to work through this.
//populate flaggedStringArr with flaggedAssessor email
stringPopPos = 0;
for (var i: number = 0; i < this.flaggedAssessor.email.length; i++) {
flaggedStringArr.push(this.flaggedAssessor.email.charAt(stringPopPos));
stringPopPos++;
}
//populate matchedStringArr with assessor email
stringPopPos = 0;
for (var i: number = 0; i < this.assessor.email.length; i++) {
matchedStringArr.push(this.assessor.email.charAt(stringPopPos));
stringPopPos++;
}
//Determine which stringArr is longer for next loop
if (flaggedStringArr > matchedStringArr) {
flaggedShortest = false;
} else {
flaggedShortest = true;
}
//compare the two string arrays. As matches are found store the letter that was matched in a new array.
//As matches are found increment a counter. If 3 or more consecutive matches are found, then a possible word has been found and matched.
//Loop duration is based off the shorter of the two string arrays
for (var i: number = 0; i < (flaggedShortest ? flaggedStringArr.length : matchedStringArr.length); i++) {
if (flaggedStringArr[i] === matchedStringArr[i]) {
storedChars.push(flaggedStringArr[i]);
matchCounter++;
} else {
matchLog.push(matchCounter);
matchCounter = 0;
}
}
//matchLog has one entry that was not pushed because loop ends
//push that entry
matchLog.push(matchCounter);
//Begin searching the match log for matches
for (var i: number = 0; i < matchLog.length; i++) {
//search for matchLog values that are greater than 3
//while searching if one of the logs values is not greater than 3, add it to a counter
//when a match is found, use the counter to start assembling a string at the appropriate location
if (matchLog[i] > 3) {
//a match has been found assemble a regex to search
//use stringTraverse to know where to start in the storedChars array
//use the value in the matchLog at position i to tell you how far along the string to go
for (var ni: number = 0; ni < (matchLog[i]); ni++) {
regExSearch += storedChars[stringTraverse + ni];
}
//The loop has constructed the string
//add the regex search term to the regexArr for later
regexArr.push(regExSearch);
//Empty regexSearch param for next match search
regExSearch = '';
//add matchLog's current value to string array in case there are future matches beyond this one
stringTraverse += matchLog[i];
} else {
stringTraverse += matchLog[i];
}
}
$('#emailToSwap').attr('id', `emailNumber${this.ownIndex}`);
//Now out of the loop cycle through and apply styles to the regex search terms
for (var i: number = 0; i < regexArr.length; i++) {
var regexObj: RegExp = new RegExp(regexArr[i], 'g');
$(`#emailNumber${this.ownIndex}`).html(
function (index, h) {
return h.replace(regexObj, `<span class="dedupe-match">${regexArr[i]}</span>`);
}
);
}
Then I realized the problem is, this only works for strings of exactly the same length, so:
JohnDoe#gmail.com
and
JohnRoe#gmail.com
will match on John, and oe#gmail.com
but if I offset either one by one letter i.e.
JohnDoe#gmail.com
and
SJohnRoe#gmail.com
now my code will match on absolutely nothing, because it compares one character at a time to another for each string. So now I'm at a loss for how to continue.
I'm working with angularJs, jQuery, and Typescript if that's helpful information for anyone.
I want validate text box with particular range having format like :
1-99
I am using regex :
/^(?:100|[1-9]\d|\d)-(?:100|[1-9]\d|\d)$/
It works for me but little problem that is it accept this:
55-50
And it shouldn't, this is wrong.
how can I correct this?
As it has been told early regexp is not the method for validating ranges. The better way is to use if/else statements. But you are not restricted in usage of regexp for validating input string on the particular format.
F.i., if you'd like to enable the end user to enter the range in the format number1-number2, you could check the string for compliance to this format and check its parts for complaince to the condition number1 <= number2. If all these checks are done you could do something useful or decline, if checks are fail.
function validRange(rangeStr, min, max) {
var m = rangeStr.match(/^([0-9]+)-([0-9]+)$/);
if ( m && m[1] >= min && m[2] <= max && m[1] <= m[2] ) {
return true;
}
return false;
}
var s = '1-99';
var s = '55-50';
if ( validRange(s, 1, 99) ) {
// do something useful
}
The code above is just skeleton for the further improvements but it can be used now. But the code could be too complicated, if you or your customers will request to implement something more complex like ability to enter single number, lists of numbers (separated with comma, semicolons etc), mixed ranges or any combination of all of them.
Because you need to check validation between the both number you have to use logical operations to check if the forst number is less than second, so you couldn't use regex in this case instead use if/else statement :
var input = "55-50";
if(input.indexOf('-')){
var input_arr = input.split('-');
if(input_arr.length==2 && parseInt(input_arr[0])<parseInt(input_arr[1]))
alert("Accepted");
else
alert("Not accepted");
}
I've am using jQuery validation plugin to validate a mobile phone number and am 2/3 of the way there.
The number must:
Not be blank - Done,
Be exactly 11 digits - Done,
Begin with '07' - HELP!!
The required rule pretty much took care of itself and and I managed to find the field length as a custom method that someone had shared on another site.
Here is the custom field length code. Could anyone please suggest what code to add where to also require it begin with '07'?
$.validator.addMethod("phone", function(phone_number, element) {
var digits = "0123456789";
var phoneNumberDelimiters = "()- ext.";
var validWorldPhoneChars = phoneNumberDelimiters + "+";
var minDigitsInIPhoneNumber = 11;
s=stripCharsInBag(phone_number,validWorldPhoneChars);
return this.optional(element) || isInteger(s) && s.length >= minDigitsInIPhoneNumber;
}, "* Your phone number must be 11 digits");
function isInteger(s)
{ var i;
for (i = 0; i < s.length; i++)
{
// Check that current character is number.
var c = s.charAt(i);
if (((c < "0") || (c > "9"))) return false;
}
// All characters are numbers.
return true;
}
function stripCharsInBag(s, bag)
{ var i;
var returnString = "";
// Search through string's characters one by one.
// If character is not in bag, append to returnString.
for (i = 0; i < s.length; i++)
{
// Check that current character isn't whitespace.
var c = s.charAt(i);
if (bag.indexOf(c) == -1) returnString += c;
}
return returnString;
}
$(document).ready(function(){
$("#form").validate();
});
The code in the question seems a very complicated way to work this out. You can check the length, the prefix and that all characters are digits with a single regex:
if (!/^07\d{9}$/.test(num)) {
// "Invalid phone number: must have exactly 11 digits and begin with "07";
}
Explanation of /^07\d{9}$/ - beginning of string followed by "07" followed by exactly 9 digits followed by end of string.
If you wanted to put it in a function:
function isValidPhoneNumber(num) {
return /^07\d{9}$/.test(num);
}
If in future you don't want to test for the prefix you can test just for numeric digits and length with:
/^\d{11}$/
You could use this function:
function checkFirstDigits(s, check){
if(s.substring(0,check.length)==check) return true;
return false;
}
s would be the string, and check would be what you are checking against (i.e. '07').
Thanks for all the answers. I've managed to come up with this using nnnnnn's regular expression. It gives the custom error message when an incorrect value is entered and has reduced 35 lines of code to 6!
$.validator.addMethod("phone", function(phone_number, element) {
return this.optional(element) || /^07\d{9}$/.test(phone_number);
}, "* Must be 11 digits and begin with 07");
$(document).ready(function(){
$("#form").validate();
});
Extra thanks to nnnnnn for the regex! :D
Use indexOf():
if (digits.indexOf('07') != 0){
// the digits string, presumably the number, didn't start with '07'
}
Reference:
indexOf().
I am working on a JS function for generating password strings, it takes four parameters for a-z lowercase, A-Z uppercase, 0-9 and punctuations. I put together a base string, like this:
function genpwd(azlc,azuc,num,pun,len) {
var chars = "";
if (azlc) chars += "abcdefghijklmnopqrstuvwxyz";
if (azuc) chars += "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
if (num) chars += "012345678901234567890123";
if (pun) chars += "!##%&()=?+-_.:,;*{}[]^$/";
Then I loop through the base string (given length of the password) and randomly picking out chars to a new string and returns this as the output password.
for(i=0;i<len;i++) {
nextChar = chars.charAt(Math.floor(Math.random()*charsN));
password += nextChar;
}
return password;
}
This is a really simple way of generating a random string, but it does not guarantee that at least one char from each "char group" actually is included in the output string.
I have looked at some other examples on how to use regexps, but can't figure out how to modify them to work the way I want. I'm thinking the "easiest" way to solve this is probably by using a regexp - something like this:
if (password.magic_regexp_stuff) {
return password;
} else {
password = genpwd(azlc,azuc,num,pun,len);
}
Am I on the right track?
Can anyone help me with the regexp?
UPDATE:
Ok, so after some valuable input from all of you, I ended up with this function.
I also switched from mVChr suggestion (thanks man!) to the one posted by Py, so I'm pretty sure the "statistics problem" (don't have any other word for it) pointed out by NobRuked won't be a problem any more. Can someone confirm this please? :)
I also had to modify the function's parameters and approach to the sets of chars to be used. Any suggestions on improvements?
function passwd(len, azlc, azuc, num, pun) {
var len = parseInt(len),
ar = [],
checker = [],
passwd = '',
good = true,
num, num2,
sets = 0;
if(!len || len < 4) len = 12;
if(!azlc && !azuc && !num && !pun) { azlc = 1; azuc = 1; num = 1; pun = 1; }
if (azlc) {
ar.push("abcdefghijklmnopqrstuvwxyz");
checker.push(0);
sets++;
}
if (azuc) {
ar.push("ABCDEFGHIJKLMNOPQRSTUVWXYZ");
checker.push(0);
sets++;
}
if (num) {
ar.push("0123456789");
checker.push(0);
sets++;
}
if (pun) {
ar.push("!##%&()=?+-_.:,;*{}[]^$/");
checker.push(0);
sets++;
}
for (var i=0;i<len;i++){
num=rand(0,sets);
num2=rand(0,ar[num].length);
passwd+=ar[num][num2];
checker[num]=1;
}
for (var i=0;i<sets;i++){
if(checker[i]===0)
good=false;
}
if (good){
return passwd;
}
else{
return passwd(len);
}
}
Many thanks to everyone for your help, it's appreciated!
I don't think regular expressions are the right tool for the job. Your attempt could theoretically loop forever (though highly unlikely) since there is no guarantee a generated password will ever match the regular expression.
I guess the easy way of making sure one character from each group is included is to explicitly include it. So, assuming your password is to be at least 4 characters long, I would suggest the following pseudo-code:
chars = charFrom(azlc) + charFrom(azuc) + charFrom(num) + charFrom(pun)
do the following, length_of_password - 4 times:
chars += charFrom(azlc + azuc + num + pun)
chars = shuffle(chars)
Implementation of charFrom() and shuffle() are left as an exercise to the reader.
To do this with regular expressions, you really just need (up to) four regular expressions:
function genpwd(azlc,azuc,num,pun,len) {
var chars = "", regexs = [],
testRegexs = function(pw) {
var ii;
for (ii = 0; ii < regexs.length; ii += 1) {
if (!regexs[ii].test(pw)) {
return false;
}
return true;
}
};
if (azlc) {
chars += "abcdefghijklmnopqrstuvwxyz";
regexs.push(/[a-z]/);
}
if (azuc) {
chars += "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
regexs.push(/[A-Z]/);
}
if (num) {
chars += "012345678901234567890123";
regexs.push(/0-9/);
}
if (pun) {
chars += "!##%&()=?+-_.:,;*{}[]^$/";
regexs.push(/[\!\#\#\%\&\(\)\=\?\+\-\_\.\:\,\;\*\{\}\[\]\^\$\/]/);
}
do
{
// Generate a password...
}
while (!testRegexs(password));
return password;
}
I would take another approch.
I'd have 4 strings
lower = "abcdefghijklmnopqrstuvwxyz";
upper = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
num = "0123456789";
other = "!##%&()=?+-_.:,;*{}[]^$/";
Then to generate the password, i'll take a random integer between 0 and 3, and then another integer between 0 and the choosen string to take a character.
To check if everything was taken, I'd just have to check that the 4 integer have been taken in the first random choice.
the code would look like
function psswd(len){
var ar=[lower,upper,num,other],
checker=[0,0,0,0],
passwd="",
good=true,
num,num2;
for (var i=0;i<len;i++){
num=randomInt(0,3);
num2=randomInt(0,ar[num].length);
passwd+=ar[num][num2];
checker[num]=1;
}
for (var i=0;i<3;i++){
if(checker[i]===0)
good=false;
}
if (good){
return passwd;
}
else{
return psswd(len);
}
}
Might not be optimal, but no regexp needed.
function genpwd(azlc,azuc,num,pun,len) {
var sets = [],
pw = [],
i, j, t;
if (azlc) sets.push("abcdefghijklmnopqrstuvwxyz");
if (azuc) sets.push("ABCDEFGHIJKLMNOPQRSTUVWXYZ");
if (num) sets.push("0123456789");
if (pun) sets.push("!##%&()=?+-_.:,;*{}[]^$/");
while (len) {
pw.push(sets[len%sets.length][~~(Math.random()*sets[len%sets.length].length)]);
len--;
}
i = pw.length;
while (--i) {
j = ~~(Math.random()*(i+1));
t = pw[j];
pw[j] = pw[i];
pw[i] = t;
}
return pw.join('');
}
EDIT: added shuffle
Just for the record, validation can be done in a single regex:
/^(?=.*[A-Z])(?=.*[a-z])(?=.*\d)(?=.*[!##%&()=?+_.:,;*{}\[\]^$\/-])/.test(mypassword)
As for your password generation scheme, see this.
This is the final version of my function (I also updated the question post with some info).
Thanks again to everyone helping me! :)
/**
* Generate Password
* ---
* Generates a random text string containing characters types according to given
* parameters. Uses all character types by default. Output string length will at
* minimum be the number of character types currently in use.
*
* #param int Output string lenght
* #param bool Use letters a-z lower case
* #param bool Use letters A-Z upper case
* #param bool Use numbers 0-9
* #param bool Use punctuations (!##$?*...)
* #return string Generated password string
*/
function genPwd(l,w,x,y,z){
var a=[],c=[],p='',g=true,n1,n2,s=0,i=0;
if(!w&&!x&&!y&&!z){w=1,x=1,y=1,z=1;}
if(w){c.push(0);s++;a.push("abcdefghijklmnopqrstuvwxyz");}
if(x){c.push(0);s++;a.push("ABCDEFGHIJKLMNOPQRSTUVWXYZ");}
if(y){c.push(0);s++;a.push("012345678901234567890123456789");}
if(z){c.push(0);s++;a.push("!##%&/(){}[]=?+*^~-_.:,;");}
if(l<s){l=s;}for(i=0;i<l;i++){n1=Math.floor(Math.random()*(s-0));
n2=Math.floor(Math.random()*(a[n1].length-0));p+=a[n1][n2];c[n1]=1;}
for(i=0;i<s;i++){if(c[i]===0)g=false;}
if(g){return p;}else{return genPwd(l,w,x,y,z);}
}