Regular expression for accepting numbers and letters, regardless of sequence

Asked

Viewed 5,040 times

5

I have the following problem, I need to validate a password field with at least 8 characters, being at least 2 numbers and the rest letters, they can be in any position.

12abcdef abcdef12

The expression I made works in the above cases:

function vSenha(senha) {
    return /^(((?=.*[a-z])(?=.*\d{2,}).{8,16})|(?=.*\d{2,})(?=.*[a-z]).{8,16})$/.test(senha);

}

But if I put a letter and number interspersed, the expression doesn’t work. I was trying to assemble an operator OR with regex but not successful.

Ex of how it doesn’t work:

1a2b3c4d

  • Can there be other characters than letters? with the!?#€%&?

  • 1

    @Sergio, no no, it’s just letters, I forgot to ask the question

7 answers

4

I think it will be difficult a regex that covers all cases. It would be easier to count the characters and numbers and check at the end.

Suggestion:

var testes = ["12abcdef", "abcdef12", "1a2b3c4d", "abcdefghij"];

function vSenha(senha) {
  var numeros = 0;
  var letras = 0;
  var caracteres = senha.split('');
  for (let car of caracteres) {
    if (car.match(/\d/)) numeros++; // se for numero
    else if (car.match(/\w/)) letras++; // se não for numero, pode ser letra
  }
  return numeros >= 2 && (numeros + letras) == senha.length;

}

testes.forEach(teste => console.log(teste, vSenha(teste)));

2

I believe that it is simpler to use two regex one for numbers and the other only for letters and then check the minimum values of each category, since the capture (with the parameter g) returns an array. I added an extra check that captures other characters like #@,._ if you find any of them makes the password invalid.

function validarSenha(senha){

    if(senha.length === 0 || senha.match(/\W|_/)){
        console.log('senha invalida');
        return false;
    } 

    var numeros = senha.match(/\d/g) ? senha.match(/\d/g).length : 0;
    var letras = senha.match(/[a-zA-Z]/g) ? senha.match(/[a-zA-Z]/g).length : 0;
    var totalCaracteresValidos = numeros + letras;
    var tamanhoMinimoSenha = 8;


    if(totalCaracteresValidos >= tamanhoMinimoSenha && numeros >= 2 && !senha.match(/\W|_/)){
       console.log('senha valida');
    }else{
       console.log('senha invalida');
    }
}


validarSenha('12345AAAA'); //válida
validarSenha('12345AAAA#'); //invalida devido ao '#' mesmo tendo 8 mais de caracteres sendo pelo menos 2 dígitos.
validarSenha('1ABC2ccD'); //válida
  • 2

    Is there a problem to answer? can I correct.

1

As stated here, only with regex it will be difficult to encompass all the possibilities, therefore, follows two ways to do this: 1st form: in this detail a little more to know how many letters, how many numbers and how many invalid characters were inserted.

jQuery(function($){
  jQuery('#validarSenha').click(function(){
    var senha = $('#senha').val();
    var tamanhoSenha = senha.length;
    //Verifica o tamanho da senha
    if (tamanhoSenha < 8){
      $("#ValidadeSenha").html("Senha Inválida - A senha deve conter pelo menos 8 dígitos!!");
    }
    else {
      var qtdNumeros = 0;
      var qtdLetras = 0;
      var caractereInvalido = 0;
      i = 0;
      //Percorre toda a string verificando cada caractere
      while (i < tamanhoSenha){
        //Verifica se o caractere atual é número letra
        if (senha.charAt(i).match(/\d/)) {
          qtdNumeros++; 
        }
        //Verifica se o caractere atual é letra
        else if (senha.charAt(i).match(/\w/)){
          qtdLetras++;
        }
        //Verifica se o caractere atual é um caractere que não seja letra ou número
        else{
          caractereInvalido++;
        }		
        i++;
      }

      if (qtdNumeros < 2 || caractereInvalido > 0 ){
        $("#ValidadeSenha").html("Senha Inválida - A senha deve conter pelo menos 2 números e deve conter somente letras e números!!");
        console.log("Quantidade de letras: "+qtdLetras);
        console.log("Quantidade de números: "+qtdNumeros);
        console.log("Quantidade de caracteres inválidos: "+caractereInvalido);
      }
      else {
        $("#ValidadeSenha").html("Senha válida!");
        console.log("Quantidade de letras: "+qtdLetras);
        console.log("Quantidade de números: "+qtdNumeros);
      }
    }
  });
});
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet"/>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>

Senha <input type="text" id="senha" name="senha" ><p id="ValidadeSenha"></p>
			
<br>
<button id="validarSenha" class="btn btn-warning" type="button">Validar Senha</button>

2nd form: simpler, it is basically that the @Sergio posted, but lacked only the question of validation of minimum size 8 digits, with this, the function is passing passwords with less than 8 digits, so I only complemented the function adding the 8 digits check in the last line of the function:

$(document).ready(function() {
  jQuery('#validarSenha').click(function(){
    var senha = $('#senha').val();
    if (vSenha(senha) == true){
      $("#ValidadeSenha").html("Senha válida!");
    }
    else{
      $("#ValidadeSenha").html("Senha Inválida!");
    }
  });
  function vSenha(senha) {
    var numeros = 0;
    var letras = 0;
    var caracteres = senha.split('');
    for (let car of caracteres) {
      if (car.match(/\d/)) numeros++; // se for numero
      else if (car.match(/\w/)) letras++; // se não for numero, pode ser letra
    }
    return numeros >= 2 && (numeros + letras) == senha.length && senha.length >= 8;
  }
});
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet"/>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>

Senha <input type="text" id="senha" name="senha" ><p id="ValidadeSenha"></p>
			
<br>
<button id="validarSenha" class="btn btn-warning" type="button">Validar Senha</button>

1

You can even set up a REGEX for this, but it would be huge, precisely because not be regular, since the numbers can be anywhere in the password.

The ideal in your case is to build an algorithm like suggested by @Rgio

Suggestion from me :

function checkPass(pass){
  var len = pass.length;
  var num = pass.replace(/\D/g, '').length;
  var wrong = pass.replace(/[a-z0-9]/gi, '').length; // REMOVE TUDO QUE VOCÊ QUER, CASO HAJA CARACTERES QUE VOCÊ NÃO QUER ELES ESTARAO AQUI.
  return len >= 8 && num >= 2 && !(wrong > 0);
}

var testes = ["12abcdef", "abcdef12", "1a2b3c4d", "abcdefghij", "1a2b$3c4d"];

for(var i=0;i<testes.length;i++){
  console.log(testes[i], checkPass(testes[i]));
}

  • you can show me why the regex ^((?=(?:.?[a-z]){1,})(?=(?:.?[0-9]){2,})).+ $ is worse than this algorithm?

  • In computer theory, regular languages are a strict subset of context-free languages, but implementations of regular expressions in conventional programming languages are more powerful. Perl compatible regex engines are Turing complete. A regex can actually match beyond regular languages.

1

Conditions

  1. 8+ characters, numbers or letters only:

    /^[a-z\d]{8,}$/i
    
  2. Minimum 2 numbers:

    /^(?:.*?\d){2}/
    
  3. Minimum 1 letter:

    /^.*?[a-z]/i
    


All together

/^(?=.*?[a-z])(?=(?:.*?\d){2})[a-z\d]{8,}$/i

To join different conditions in the same expression, you need a Lookahead. Lookahead does not match characters at the current position, but gives a peek ahead. If the assertion corresponds, it returns to the current position, without consuming characters.


Code

function vSenha(senha) {
    var regex = /^(?=.*?[a-z])(?=(?:.*?\d){2})[a-z\d]{8,}$/i;
    return regex.test(senha);
}
  • I was going to post the same REGEX then I saw that you had already put, the only difference is that I was using the .*? (greedy operator)

  • @It is true that it is a little more efficient. The solution with the best result would be to use a denied list (Jsperf). I chose to .* to keep it simple, but I see the AP has already used .*?. I edited.

  • I checked the tester you sent over and over and the operator .*? was even faster. By logic the denied list would be slower as it would still include checking if the character is not in the list, already the . captures everything then does not have this check. Testing

  • @William the point also checks that does not marry \n. The denied list usually has a longer build time but a shorter run time. However, like any regex, this depends on many things, many optimizations, and especially the string. My results FF53, Chrome59, Chrome57/Android N... This increases for valid cases: Jsperf 2.

1


After some tests the following expression solved my problem:

function vSenha(senha) {
    return /^((?=(?:.*?[a-z]){1,})(?=(?:.*?[0-9]){2,})).{8,16}$/.test(senha);
}

The cases below work:

12cbahda
1b3c4c3d2a
a1fewfew2

I didn’t need anything else to run the validation.

  • 1

    If it’s just numbers your regex fails, is that what you want? For example '123456789'

  • This, in the end it has to accept at least 2 numbers and the rest letters, can be 12345678910abc or abcdefghj12,

  • This regex accepts "$%()!1a2?+".

-3

Dude, it’s like because it’s a regular expression. Example link in regexr: https://regexr.com/5p75r

Explaining the regex:

  • ^(?=[a-zA-Z0-9!@#$%\^&*()]{8,32}$) ensures that from start to finish, it must have 8 to 32 characters that are between block characters
  • (?=(?:[^a-z]*[a-z])) requires it to have a character of [a-z] among the various different from it, once (implicit)
  • (?=(?:[^A-Z]*[A-Z]){1}) requires you to have a character of [A-Z] among the various different from it, once (explicitly)
  • (?=(?:[^0-9]*[0-9]){3}) requires you to have a character of [0-9] among the several different from it, three times (explicitly)
  • (?=(?:[^!@#$%&*()]*[!@#$%&*()]){1}) requires you to have a character of [!@#$%&*()] among the several different from it, once (explicitly)
  • (?:[a-zA-Z0-9!@#$%&*()]*)$ allows the disorganized combination between the rules, making it possible to realize the greetings of expressions disorderly (?=)

Browser other questions tagged

You are not signed in. Login or sign up in order to post.