javascript regex: ¿mirar detrás de la alternativa?

143

Aquí hay una expresión regular que funciona bien en la mayoría de las implementaciones de expresiones regulares:

(?<!filename)\.js$

Esto coincide con .js para una cadena que termina con .js excepto para filename.js

Javascript no tiene regex retrospectiva. ¿Alguien puede armar una expresión regular alternativa que logre el mismo resultado y funcione en javascript?

Aquí hay algunos pensamientos, pero necesita funciones de ayuda. Esperaba lograrlo solo con una expresión regular: http://blog.stevenlevithan.com/archives/mimic-lookbehind-javascript

daniel
fuente
3
Si solo necesita verificar un nombre de archivo específico o una lista de nombres de archivo, ¿por qué no usar dos verificaciones? verifique si termina en .js y luego, si lo hace, verifique que no coincida con filename.js o viceversa.
si28719e
3
Actualización: la última versión pública de Chrome (v62) incluye mirar hacia atrás (presumiblemente experimental) fuera de la caja: D Tenga en cuenta que los miradores aún están en la etapa de propuesta 3: github.com/tc39/proposal-regexp-lookbehind . Por lo tanto, puede pasar un tiempo hasta que JavaScript lo admita en todas partes. ¡Mejor tenga cuidado con el uso en la producción!
Eirik Birkeland el
2
# Actualización: ES2018 incluye aserciones de retrospectiva Más : - modo dotAll (la bandera s) - Aserciones de retrospectiva - Grupos de captura con nombre - Escapes de propiedad Unicode
Ashley Coolman
2
Solo use (?<=thingy)thingypara mirar hacia atrás positivo y (?<!thingy)thingypara mirar hacia atrás negativo . Ahora los apoya.
Константин Ван
77
@ K._ A partir de febrero de 2018, ¡ eso aún no es cierto ! Y necesitará algo de tiempo porque los navegadores y los motores deben implementar la especificación (actual en borrador).
Andre Figueiredo

Respuestas:

64

^(?!filename).+\.js funciona para mi

probado contra:

  • prueba.js partido
  • partido de blabla.js
  • filename.js no coincide

¿Se puede encontrar una explicación adecuada para esta expresión regular en Expresión regular para que coincida con la cadena que no contiene una palabra?

Look ahead está disponible desde la versión 1.5 de javascript y es compatible con todos los principales navegadores

Actualizado para coincidir con filename2.js y 2filename.js pero no filename.js

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

Benjamin Udink ten Cate
fuente
55
Esa pregunta que vinculaste habla de un problema ligeramente diferente: hacer coincidir una cadena que no contiene la palabra objetivo en ningún lado . Este es mucho más simple: hacer coincidir una cadena que no comienza con la palabra objetivo.
Alan Moore
Eso es realmente bueno, solo se pierde en casos como: filename2.js o filenameddk.js o similares. Esta no es una coincidencia, pero debería ser una coincidencia.
Daniel
9
@daniel Usted pidió una mirada atrás, no una mirada hacia adelante, ¿por qué aceptó esta respuesta?
hek2mgl
1
el dado no coincide ena.js
inetphantom
1
La expresión regular original con lookbehind no coincide 2filename.js, pero la expresión regular dada aquí sí. Una más apropiada sería ^(?!.*filename\.js$).*\.js$. Esto significa, emparejar cualquiera *.js excepto *filename.js .
weibeld
153

EDITAR: desde ECMAScript 2018 en adelante, las aserciones retrospectivas (incluso ilimitadas) son compatibles de forma nativa .

En versiones anteriores, puedes hacer esto:

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

Esto hace explícitamente lo que la expresión de retrospectiva está haciendo implícitamente: verifique cada carácter de la cadena si la expresión de retrospectiva más la expresión regular después de que no coincida, y solo entonces permita que ese carácter coincida.

^                 # 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

Otra edición:

Me duele decir (especialmente porque esta respuesta se ha votado tanto) que hay una manera mucho más fácil de lograr este objetivo. No hay necesidad de verificar la anticipación de cada personaje:

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

funciona igual de 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
fuente
Funciona en muchos casos, excepto donde hay caracteres anteriores, por ejemplo: filename.js (works-nomatch) filename2.js (works-match) blah.js (works - match) 2filename.js (no funciona - nomatch) --- una vez dicho esto, la búsqueda hacia atrás tiene la misma limitación que no me di cuenta hasta ahora ...
Daniel
9
@daniel: Bueno, tu expresión regular (con lookbehind) tampoco coincide 2filename.js. Mi expresión regular coincide exactamente en los mismos casos que su ejemplo de expresión regular.
Tim Pietzcker
Perdona mi ingenuidad pero ¿hay algún uso para el grupo que no captura aquí? Siempre he sabido que eso solo es útil cuando intento obtener una referencia para reemplazarla en una cadena. Hasta donde yo sé, esto también funcionará ^ (?! filename \ .js $). * \. Js $
Quiero respuestas
1
No del todo, esa expresión regular busca "filename.js" solo al comienzo de la cadena. Pero ^(?!.*filename\.js$).*\.js$funcionaría. Tratando de pensar en situaciones en las que el grupo nc aún podría ser necesario ...
Tim Pietzcker
Este enfoque se puede resumir como: en lugar de mirar detrás de X, ¿mirar hacia adelante a cada personaje que viene antes de X?
Zarzaparrilla
25

Supongamos que desea encontrar todo lo que intno esté precedido por unsigned:

Con soporte para mirar hacia atrás negativo:

(?<!unsigned )int

Sin soporte para la retrospectiva negativa:

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

Básicamente, la idea es tomar n caracteres anteriores y excluir la coincidencia con anticipación negativa, pero también coincide con los casos en los que no hay n caracteres anteriores. (donde n es la longitud de mirar hacia atrás).

Entonces la expresión regular en cuestión:

(?<!filename)\.js$

se traduciría a:

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

Es posible que deba jugar con grupos de captura para encontrar el lugar exacto de la cadena que le interesa o no desea reemplazar una parte específica con otra cosa.

Kamil Szot
fuente
Acabo de convertir esto: (?<!barna)(?<!ene)(?<!en)(?<!erne) (?:sin|vår)e?(?:$| (?!egen|egne))a lo (?!barna).(?!erne).(?!ene).(?!en).. (?:sin|vår)e?(?:$| (?!egen|egne))que hace el truco para mis necesidades. Simplemente proporcionando esto como otro escenario del "mundo real". Ver enlace
Eirik Birkeland
Creo que quisiste decir:((?!unsigned ).{9}|^.{0,8})int
Pansay
@pansay Sí. Gracias. Acabo de corregir mi respuesta.
Kamil Szot
2
¡Gracias por la respuesta más generalizada que funciona incluso cuando es necesario que coincida profundamente en el texto (donde ^ inicial no sería práctico)!
Milos Mrdovic
5

Si puede mirar hacia adelante pero hacia atrás, puede invertir la cadena primero y luego mirar hacia adelante. Se necesitará más trabajo, por supuesto.

Albert Friend
fuente
8
Esta respuesta realmente podría usar alguna mejora. Me parece más un comentario.
mickmackusa
2

Esta es una solución equivalente a la respuesta de Tim Pietzcker (ver también comentarios de la misma respuesta):

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

Significa, partido *.jsexcepto *filename.js.

Para llegar a esta solución, puede verificar qué patrones excluye la mirada negativa hacia atrás y luego excluir exactamente estos patrones con una mirada negativa hacia adelante.

weibeld
fuente
-1

A continuación se muestra una alternativa positiva de JavaScript detrás que muestra cómo capturar el apellido de las personas con 'Michael' como primer nombre.

1) Dado este texto:

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?";

obtener una variedad de apellidos de personas llamadas Michael. El resultado debería ser:["Jordan","Johnson","Green","Wood"]

2) Solución:

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) Verificar solución

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

Demostración aquí: http://codepen.io/PiotrBerebecki/pen/GjwRoo

También puedes probarlo ejecutando el fragmento a continuación.

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
fuente