javascript regex - regarder derrière une alternative?

143

Voici une regex qui fonctionne bien dans la plupart des implémentations de regex:

(?<!filename)\.js$

Cela correspond à .js pour une chaîne qui se termine par .js sauf pour filename.js

Javascript n'a pas de regex lookbehind. Quelqu'un est-il capable de créer une expression régulière alternative qui atteint le même résultat et fonctionne en javascript?

Voici quelques réflexions, mais a besoin de fonctions d'assistance. J'espérais y parvenir simplement avec une regex: http://blog.stevenlevithan.com/archives/mimic-lookbehind-javascript

Daniel
la source
3
si vous avez juste besoin de vérifier un nom de fichier spécifique ou une liste de noms de fichiers, pourquoi ne pas utiliser simplement deux vérifications? vérifiez s'il se termine par .js et si c'est le cas, vérifiez qu'il ne correspond pas à filename.js ou vice versa.
si28719e
3
Mise à jour: La dernière version publique de Chrome (v62) inclut (vraisemblablement expérimental) des regards en arrière prêts à l'emploi: D Notez cependant que les regards en arrière sont toujours à l'étape de proposition 3: github.com/tc39/proposal-regexp-lookbehind . Ainsi, cela peut prendre un certain temps avant que JavaScript ne le prenne partout. Mieux vaut faire attention à l'utilisation en production!
Eirik Birkeland
2
# Mise à jour: ES2018 inclut les assertions lookbehind Plus : - mode dotAll (le drapeau s) - assertions Lookbehind - Groupes de capture nommés - La propriété Unicode s'échappe
Ashley Coolman
2
Il suffit d' utiliser (?<=thingy)thingypour lookbehind positif et (?<!thingy)thingypour lookbehind négative . Maintenant, il les soutient.
Константин Ван
7
@ K._ En février 2018, ce n'est pas encore vrai !! Et il faudra un certain temps car les navigateurs et les moteurs doivent implémenter la spécification (actuelle dans le brouillon).
Andre Figueiredo

Réponses:

64

^(?!filename).+\.js travaille pour moi

testé contre:

  • correspondance test.js
  • match blabla.js
  • filename.js pas de correspondance

Une explication correcte de cette expression régulière peut être trouvée dans Expression régulière pour correspondre à une chaîne ne contenant pas de mot?

Look ahead est disponible depuis la version 1.5 de javascript et est pris en charge par tous les principaux navigateurs

Mis à jour pour correspondre à filename2.js et 2filename.js mais pas à filename.js

(^(?!filename\.js$).).+\.js

Benjamin Udink ten Cate
la source
5
Cette question que vous avez liée parle d'un problème légèrement différent: faire correspondre une chaîne qui ne contient nulle part le mot cible . Celui-ci est beaucoup plus simple: faire correspondre une chaîne qui ne commence pas par le mot cible.
Alan Moore du
C'est vraiment sympa, il ne manque que des cas comme: filename2.js ou filenameddk.js ou similaire. Ce n'est pas un match, mais cela devrait être un match.
daniel le
9
@daniel Vous avez demandé un regard en arrière, pas un regard en avant, pourquoi avez-vous accepté cette réponse?
hek2mgl
1
celui donné ne correspond pas sura.js
inetphantom
1
La regex originale avec lookbehind ne correspond pas 2filename.js, mais l'expression régulière donnée ici le fait. Un plus approprié serait ^(?!.*filename\.js$).*\.js$. Cela signifie, correspond à tout *.js sauf *filename.js .
weibeld
153

EDIT: À partir d'ECMAScript 2018, les assertions de recherche (même illimitées) sont prises en charge de manière native .

Dans les versions précédentes, vous pouvez faire ceci:

^(?:(?!filename\.js$).)*\.js$

Cela fait explicitement ce que fait implicitement l'expression lookbehind: vérifiez chaque caractère de la chaîne si l'expression lookbehind plus le regex après elle ne correspondent pas, et seulement alors autorisez ce caractère à correspondre.

^                 # Start of string
(?:               # Try to match the following:
 (?!              # First assert that we can't match the following:
  filename\.js    # filename.js 
  $               # and end-of-string
 )                # End of negative lookahead
 .                # Match any character
)*                # Repeat as needed
\.js              # Match .js
$                 # End of string

Une autre modification:

Cela me fait mal de dire (d'autant plus que cette réponse a été tellement votée) qu'il existe un moyen beaucoup plus simple d'atteindre cet objectif. Il n'est pas nécessaire de vérifier l'anticipation de chaque caractère:

^(?!.*filename\.js$).*\.js$

fonctionne aussi bien:

^                 # Start of string
(?!               # Assert that we can't match the following:
 .*               # any string, 
  filename\.js    # followed by filename.js
  $               # and end-of-string
)                 # End of negative lookahead
.*                # Match any string
\.js              # Match .js
$                 # End of string
Tim Pietzcker
la source
Fonctionne sur de nombreux cas sauf lorsqu'il y a des caractères précédents, par exemple: filename.js (works-nomatch) filename2.js (works-match) blah.js (works - match) 2filename.js (ne fonctionne pas - nomatch) --- cela dit, le regard en arrière a la même limitation que je n'avais pas réalisée jusqu'à présent ...
daniel
9
@daniel: Eh bien, votre regex (avec lookbehind) ne correspond pas non plus 2filename.js. Mon regex correspond exactement aux mêmes cas que votre exemple de regex.
Tim Pietzcker du
Pardonnez ma naïveté mais y a-t-il une utilité pour le groupe non capturant ici? J'ai toujours su que cela n'était utile que lorsque vous essayez de glaner une référence pour le remplacement dans une chaîne. Autant que je sache, cela fonctionnera aussi ^ (?! filename \ .js $). * \. Js $
Je veux des réponses
1
Pas tout à fait, cette expression régulière ne vérifie "filename.js" qu'au début de la chaîne. Mais ^(?!.*filename\.js$).*\.js$ça marcherait. Essayer de penser à des situations où le ncgroup pourrait encore être nécessaire ...
Tim Pietzcker
Cette approche peut être résumée comme suit: au lieu de regarder derrière X, regardez devant chaque personnage qui précède X?
Salsepareille le
25

Supposons que vous vouliez tout trouver intnon précédé de unsigned:

Avec prise en charge du look-behind négatif:

(?<!unsigned )int

Sans prise en charge de la rétroaction négative:

((?!unsigned ).{9}|^.{0,8})int

Fondamentalement, l'idée est de saisir n caractères précédents et d'exclure la correspondance avec une anticipation négative, mais également de faire correspondre les cas où il n'y a pas de n caractères précédents. (où n est la longueur du regard derrière).

Donc le regex en question:

(?<!filename)\.js$

se traduirait par:

((?!filename).{8}|^.{0,7})\.js$

Vous devrez peut-être jouer avec la capture de groupes pour trouver l'endroit exact de la chaîne qui vous intéresse ou vous ne voulez pas remplacer une partie spécifique par autre chose.

Kamil Szot
la source
Je viens de convertir ceci: (?<!barna)(?<!ene)(?<!en)(?<!erne) (?:sin|vår)e?(?:$| (?!egen|egne))à (?!barna).(?!erne).(?!ene).(?!en).. (?:sin|vår)e?(?:$| (?!egen|egne))qui fait l'affaire pour mes besoins. Fournir simplement cela comme un autre scénario «réel». Voir le lien
Eirik Birkeland
Je pense que vous vouliez dire:((?!unsigned ).{9}|^.{0,8})int
pansay
@pansay Oui. Je vous remercie. J'ai juste corrigé ma réponse.
Kamil Szot
2
Merci pour la réponse plus générale qui fonctionne même là où il est nécessaire de faire correspondre profondément dans le texte (où initial ^ serait peu pratique)!
Milos Mrdovic
5

Si vous pouvez regarder en avant mais en arrière, vous pouvez d'abord inverser la chaîne, puis faire une anticipation. Il faudra bien sûr faire un peu plus de travail.

Albert ami
la source
8
Cette réponse pourrait vraiment être améliorée. Cela me semble plutôt un commentaire.
mickmackusa
2

C'est une solution équivalente à la réponse de Tim Pietzcker (voir aussi les commentaires de la même réponse):

^(?!.*filename\.js$).*\.js$

Cela signifie, match *.jssauf *filename.js.

Pour arriver à cette solution, vous pouvez vérifier quels modèles le lookbehind négatif exclut, puis exclure exactement ces modèles avec une anticipation négative.

Weibeld
la source
-1

Vous trouverez ci-dessous un aperçu positif de l'alternative JavaScript montrant comment capturer le nom de famille des personnes avec `` Michael '' comme prénom.

1) Compte tenu de ce texte:

const exampleText = "Michael, how are you? - Cool, how is John Williamns and Michael Jordan? I don't know but Michael Johnson is fine. Michael do you still score points with LeBron James, Michael Green Miller and Michael Wood?";

obtenir un tableau de noms de famille de personnes nommées Michael. Le résultat devrait être:["Jordan","Johnson","Green","Wood"]

2) Solution:

function getMichaelLastName2(text) {
  return text
    .match(/(?:Michael )([A-Z][a-z]+)/g)
    .map(person => person.slice(person.indexOf(' ')+1));
}

// or even
    .map(person => person.slice(8)); // since we know the length of "Michael "

3) Vérifier la solution

console.log(JSON.stringify(    getMichaelLastName(exampleText)    ));
// ["Jordan","Johnson","Green","Wood"]

Démo ici: http://codepen.io/PiotrBerebecki/pen/GjwRoo

Vous pouvez également l'essayer en exécutant l'extrait ci-dessous.

const inputText = "Michael, how are you? - Cool, how is John Williamns and Michael Jordan? I don't know but Michael Johnson is fine. Michael do you still score points with LeBron James, Michael Green Miller and Michael Wood?";



function getMichaelLastName(text) {
  return text
    .match(/(?:Michael )([A-Z][a-z]+)/g)
    .map(person => person.slice(8));
}

console.log(JSON.stringify(    getMichaelLastName(inputText)    ));

Piotr Berebecki
la source