web-dev-qa-db-fra.com

Correspondance partielle d'une chaîne à une regex

Supposons que je dispose de l'expression régulière suivante: /abcd/Supposons que je veuille vérifier l'entrée utilisateur par rapport à cette expression régulière et interdire la saisie de caractères non valides dans l'entrée. Lorsque l'utilisateur entre "ab", la correspondance avec l'expression rationnelle échoue, mais je ne peux pas refuser l'entrée de "a" puis de "b", car l'utilisateur ne peut pas saisir les 4 caractères à la fois (à l'exception du copier/coller). Donc, ce dont j'ai besoin ici, c'est d'une correspondance partielle qui vérifie si une chaîne incomplète peut potentiellement correspondre à une regex.

Java a quelque chose dans ce but: .hitEnd() (décrit ici http://glaforge.appspot.com/article/incomplete-string-regex-matching ) python ne le fait pas en natif mais a ce paquet qui fait le travail : https://pypi.python.org/pypi/regex .

Je n'ai trouvé aucune solution pour cela dans js. Il a été demandé il y a des années: Javascript RegEx correspondance partielle Et même avant cela: Vérifier si string est le préfixe d'un Javascript RegExp

P.S. regex est personnalisé, supposons que l'utilisateur entre le regex lui-même, puis tente de saisir un texte correspondant à ce regex. La solution doit être une solution générale qui fonctionne pour les expressions rationnelles entrées au moment de l’exécution.

14
Sassan

On dirait que vous avez de la chance, j'ai déjà implémenté ce genre de choses dans JS (ce qui fonctionne pour les patterns la plupart - peut-être que ça vous suffira). Voir ma réponse ici . Vous y trouverez également une démonstration de travail.

Il n'est pas nécessaire de dupliquer le code complet ici, je vais simplement énoncer le processus global:

  • Analyser la regex d'entrée et effectuer quelques remplacements. Il n'est pas nécessaire de gérer les erreurs car vous ne pouvez pas avoir de modèle non valide dans un objet RegExp dans JS.
  • Remplacez abc par (?:a|$)(?:b|$)(?:c|$)
  • Faites la même chose pour tous les "atomes". Par exemple, un groupe de caractères [a-c] deviendrait (?:[a-c]|$)
  • Garder les ancres telles quelles
  • Gardez les regards négatifs tels quels

Si JavaScript avait des fonctionnalités regex plus avancées, cette transformation n'aurait peut-être pas été possible. Mais avec ses fonctionnalités limitées, il peut gérer la plupart des expressions rationnelles en entrée. Cela donnera des résultats incorrects sur les expressions rationnelles avec références arrières, cependant, si votre chaîne d'entrée se termine au milieu d'une correspondance en référence arrière (similaire à la correspondance de ^(\w+)\s+\1$ avec hello hel).

8

Je pense que vous devez avoir 2 regex, un pour taper /a?b?c?d?/ et un pour tester à la fin tout en collant ou en laissant une entrée /abcd/

Ceci testera le numéro de téléphone valide:

const input = document.getElementById('input')

let oldVal = ''
input.addEventListener('keyup', e => {
  if (/^\d{0,3}-?\d{0,3}-?\d{0,3}$/.test(e.target.value)){
    oldVal = e.target.value
  } else {
    e.target.value = oldVal
  }
})
input.addEventListener('blur', e => {
  console.log(/^\d{3}-?\d{3}-?\d{3}-?$/.test(e.target.value) ? 'valid' : 'not valid')
})
<input id="input">

Et c'est le cas pour le nom de famille

const input = document.getElementById('input')

let oldVal = ''
input.addEventListener('keyup', e => {
  if (/^[A-Z]?[a-z]*\s*[A-Z]?[a-z]*$/.test(e.target.value)){
    oldVal = e.target.value
  } else {
    e.target.value = oldVal
  }
})
input.addEventListener('blur', e => {
  console.log(/^[A-Z][a-z]+\s+[A-Z][a-z]+$/.test(e.target.value) ? 'valid' : 'not valid')
})
<input id="input">

1
Maciej Kozieja

C’est la solution difficile pour ceux qui pensent qu’il n’ya pas de solution du tout: implémentez la version python ( https://bitbucket.org/mrabarnett/mrab-regex/src/4600a157989dc1671e4415ebe57aac53cfda2d8a/regex_regex/regex__regex/_gegex default & fileviewer = file-view-default ) en js. Donc c'est possible. Si quelqu'un a une réponse plus simple, il gagnera la prime.

Exemple utilisant le module python (expression régulière avec référence arrière):

$ pip install regex
$ python
>>> import regex
>>> regex.Regex(r'^(\w+)\s+\1$').fullmatch('abcd ab',partial=True)
<regex.Match object; span=(0, 7), match='abcd ab', partial=True>
0
Sassan

Je soupçonne fortement (bien que je ne sois pas sûr à 100%) que le cas général de ce problème n'ait pas de solution de la même manière que le fameux "problème Haltin" de Turing (voir Problème indécidable ). Et même s’il existe une solution, ce ne sera probablement pas ce que les utilisateurs veulent réellement et, par conséquent, en fonction de votre rigueur, vous obtiendrez un UX mauvais à horrible.

Exemple:

Supposons que "RegEx cible" est [a,b]*c[a,b]*. Supposons également que vous ayez produit un "test RegEx" [a,b]*c?[a,b]* raisonnable au premier coup d'œil (deux noms c dans la chaîne sont incorrects, yeah?) Et supposez que l'utilisateur actuel est aabcbb mais il existe un type de frappe ce que l'utilisateur souhaitait en réalité, c'est aacbbb. Il existe plusieurs façons de corriger cette faute de frappe:

  • supprimer c et l'ajouter avant le premier b - fonctionnera correctement
  • supprime d'abord b et ajoute après c - fonctionnera correctement
  • ajoutez c avant le premier b, puis supprimez l'ancien - Oups, nous interdisons cette entrée comme invalide et l'utilisateur deviendra fou parce qu'aucun humain normal ne peut comprendre une telle logique. 

Notez également que votre hitEnd aura le même problème ici, sauf si vous interdisez à l'utilisateur de saisir des caractères au milieu de la zone de saisie, ce qui constitue un autre moyen de créer une interface utilisateur horrible.

Dans la vie réelle, il y aurait beaucoup d'exemples beaucoup plus compliqués qu'aucune de vos heuristiques intelligentes ne pourra prendre en compte correctement et va donc déranger les utilisateurs.

Alors que faire? Je pense que la seule chose que vous pouvez faire et que vous obtenez toujours une UX raisonnable est la chose la plus simple que vous puissiez faire, c’est-à-dire analyser votre "RegEx cible" pour déterminer le jeu de caractères autorisés et créer votre "test RegEx" [set of allowed chars]*. Et oui, si le "RegEx cible" contient . wildcart, vous ne pourrez pas effectuer de filtrage raisonnable.

0
SergGr