web-dev-qa-db-fra.com

Regex pour correspondre à exactement 5 chiffres et un espace optionnel

J'ai récemment eu besoin de créer une expression régulière pour vérifier les entrées en JavaScript. L'entrée peut comporter 5 ou 6 caractères et doit contenir exactement 5 chiffres et un espace optionnel, qui peut figurer n'importe où dans la chaîne. Je ne suis pas du tout au courant des regex et bien que j'aie essayé de trouver un meilleur moyen, je me suis retrouvé avec ceci: 

(^\d{5}$)|(^ \d{5}$)|(^\d{5} $)|(^\d{1} \d{4}$)|(^\d{2} \d{3}$)|(^\d{3} \d{2}$)|(^\d{4} \d{1}$)  

Cela fait ce dont j'ai besoin, donc les entrées autorisées sont (si 0 est un nombre) 

'00000'  
' 00000'  
'0 0000'  
'00 000'  
'000 00'  
'0000 0'  
'00000 '

Je doute que ce soit le seul moyen de réaliser un tel couplage avec regex, mais je n'ai pas trouvé le moyen de le faire de manière plus propre. Ma question est donc: comment cela peut-il être mieux écrit?

Je vous remercie.

Modifier:
Alors, c'est possible! La réponse de Tom Lord fait ce dont j'avais besoin avec des expressions régulières, alors je l'ai marquée comme une réponse correcte à ma question. 

Cependant, peu après avoir posté cette question, je me suis rendu compte que je ne pensais pas bien, car toutes les autres entrées du projet étaient facilement "validables" avec regex, je pensais immédiatement pouvoir valider celle-ci avec celle-ci également. 

Il se trouve que je pourrais juste faire ceci: 

const validate = function(value) {
    const v = value.replace(/\s/g, '')
    const regex = new RegExp('^\\d{5}$');
    return regex.test(v);
}  

Merci à tous pour les réponses et les idées cool! :) 

Edit2: J'ai oublié de mentionner un détail assez important, à savoir que la saisie est limitée, de sorte que l'utilisateur ne peut saisir que 6 caractères au maximum. Mes excuses.

18
EyfI

Remarque: L'utilisation d'une expression régulière pour résoudre ce problème peut ne pas être la meilleure réponse. Comme répondu ci-dessous , il se peut que plus facile de simplement compter les chiffres et les espaces avec une fonction simple!

Cependant, puisque la question demandait une réponse rationnelle, et dans certains cas, __. Dans certains scénarios, vous pouvez être forcé résoudre ce problème avec un regex (par exemple, si vous êtes attaché à la mise en œuvre d'une certaine bibliothèque), le suivant La réponse peut être utile:

Cette expression rationnelle correspond aux lignes contenant exactement 5 chiffres:

^(?=(\D*\d){5}\D*$)

Cette regex correspond aux lignes contenant un espace optionnel:

^(?=[^ ]* ?[^ ]*$)

Si nous les mettons ensemble, et nous nous assurons également que la chaîne contient uniquement chiffres et espaces ([\d ]*$), nous obtenons:

^(?=(\D*\d){5}\D*$)(?=[^ ]* ?[^ ]*$)[\d ]*$

Vous pouvez également utiliser [\d ]{5,6} au lieu de [\d ]* à la fin de ce modèle, avec le même effet.

Démo

Explication:

Cette expression régulière utilise lookaheads . Ce sont des correspondeurs de motif de largeur nulle, ce qui signifie que les deux parties du motif sont "ancrées" au début de la chaîne.

  • \d signifie "n'importe quel chiffre" et \D signifie "n'importe quel non-chiffre".

  • signifie "espace", et [^ ] signifie "tout non-espace".

  • Le \D*\d est répété 5 fois, afin de s’assurer que la chaîne contient exactement 5 chiffres.

Voici une visualisation de la regex en action:

 regex visualisation

Notez que si vous souhaitiez réellement que "l'espace optionnel" inclue des éléments tels que des tabulations, vous pouvez utiliser plutôt \s et \S.


Mise à jour: Étant donné que cette question semble avoir suscité un vif intérêt, je voulais clarifier quelque chose à propos de cette réponse.

Il existe plusieurs "simples" " variantes de solutions à ma réponse ci-dessus, telles que:

// Only look for digits and spaces, not "non-digits" and "non-spaces":
^(?=( ?\d){5} *$)(?=\d* ?\d*$)

// Like above, but also simplifying the second lookahead:
^(?=( ?\d){5} *$)\d* ?\d*

// Or even splitting it into two, simpler, problems with an "or" operator: 
^(?:\d{5}|(?=\d* \d*$).{6})$

Démos de chaque ligne ci-dessus: 123

Ou même, si nous pouvons supposons que la chaîne ne compte pas plus de 6 caractères} alors même cela suffit:

^(?:\d{5}|\d* \d*)$

Dans cet esprit, alors pourquoi {pourrait} _ vouloir utiliser la solution originale, pour des problèmes similaires? Parce que c'est generic. Regardez à nouveau ma réponse originale, réécrite avec espace libre :

^
(?=(\D*\d){5}\D*$) # Must contain exactly 5 digits
(?=[^ ]* ?[^ ]*$)  # Must contain 0 or 1 spaces
[\d ]*$            # Must contain ONLY digits and spaces

Ce modèle d’utilisation d’anticipations successives peut être utilisé dans différents scénarios , pour écrire des modèles très structurés et (peut-être étonnamment) faciles à étendre.

Par exemple, supposons que les règles aient changé et que vous souhaitiez maintenant faire correspondre 2-3 espaces, 1 . et un nombre quelconque de tirets. C'est en fait très facile de mettre à jour la regex:

^
(?=(\D*\d){5}\D*$)       # Must contain exactly 5 digits
(?=([^ ]* ){2,3}[^ ]*$)  # Must contain 2 or 3 spaces
(?=[^.]*\.[^.]*$)        # Must contain 1 period
[\d .-]*$   # Must contain ONLY digits, spaces, periods and hyphens

... En résumé, il y a are _ solutions "plus simples", et peut-être une meilleure solution non-regex au problème spécifique de OP. Mais ce que j’ai fourni est un modèle de conception générique et extensible permettant d’apparier des modèles de cette nature.

18
Tom Lord

Je suggère de vérifier d’abord exactement cinq chiffres ^\d{5}$ OR et de prévoir un espace entre les chiffres ^(?=\d* \d*$) sur six caractères .{6}$.

La combinaison de ces expressions partielles donne ^\d{5}$|^(?=\d* \d*$).{6}$:

let regex = /^\d{5}$|^(?=\d* \d*$).{6}$/;

console.log(regex.test('00000'));   // true
console.log(regex.test(' 00000'));  // true
console.log(regex.test('00000 '));  // true
console.log(regex.test('00 000'));  // true
console.log(regex.test('  00000')); // false
console.log(regex.test('00000  ')); // false
console.log(regex.test('00  000')); // false
console.log(regex.test('00 0 00')); // false
console.log(regex.test('000 000')); // false
console.log(regex.test('0000'));    // false
console.log(regex.test('000000'));  // false
console.log(regex.test('000 0'));   // false
console.log(regex.test('000 0x'));  // false
console.log(regex.test('0000x0'));  // false
console.log(regex.test('x00000'));  // false

Vous pouvez également faire correspondre les expressions partielles séparément via, par exemple:

/^\d{5}$/.test(input) || input.length == 6 && /^\d* \d*$/.test(input)
8
le_m

Cela me semble plus intuitif et est O (n)

function isInputValid(input) {
    const length = input.length;
    if (length != 5 && length != 6) {
        return false;
    }

    let spaceSeen = false;
    let digitsSeen = 0;
    for (let character of input) {
        if (character === ' ') {
            if (spaceSeen) {
                return false;
            }
            spaceSeen = true;
        }
        else if (/^\d$/.test(character)) {
            digitsSeen++;
        }
        else {
            return false;
        }
    }

    return digitsSeen == 5;
}
7
JonathanR

Voici un regex simple pour faire le travail:

^(?=[\d ]{5,6}$)\d*\s?\d*$

Explication:

^ affirme la position au début de la chaîne

Lookahead positif (? = [\ D] {5,6} $)

Affirmez que le regex ci-dessous correspond

Correspond à un seul caractère présent dans la liste ci-dessous [\ d] {5,6}

{5,6} Quantificateur - Correspondance entre 5 et 6 fois, autant de fois que possible, en donnant au besoin (gourmand)

\ d correspond à un chiffre (égal à [0-9]) correspond littéralement au caractère (sensible à la casse)

$ assert la position à la fin de la chaîne \d * correspond à un chiffre (égal à [0-9])

  • Quantificateur - Correspond entre des temps nuls et illimités, autant de fois que possible, en rendant au besoin (gourmand)

\ s correspond à n'importe quel caractère d'espacement (égal à [\ r\n\t\f\v])

\ d * correspond à un chiffre (égal à [0-9])

  • Quantificateur - Correspond entre des temps nuls et illimités, autant de fois que possible, en rendant au besoin (gourmand)

$ affirme la position à la fin de la chaîne

1
John Smith

Vous pouvez le diviser en deux:

var input = '0000 ';

if(/^[^ ]* [^ ]*$/.test(input) && /^\d{5,6}$/.test(input.replace(/ /, '')))
  console.log('Match');

1
Thomas Ayoub
string="12345 ";
if(string.length<=6 && string.replace(/\s/g, '').length<=5 && parseInt(string,10)){
  alert("valid");
}

Vous pouvez simplement vérifier la longueur et si c'est un numéro valide ...

0
Jonas Wilms

Voici comment je le ferais sans regex:

string => [...string].reduce(
    ([spaces,digits], char) =>
        [spaces += char == ' ', digits += /\d/.test(char)],
    [0,0]
).join(",") == "1,5";
0
corvus_192