¿Hay alguna manera de lograr el equivalente de una mirada hacia atrás negativa en las expresiones regulares de JavaScript? Necesito hacer coincidir una cadena que no comienza con un conjunto específico de caracteres.
Parece que no puedo encontrar una expresión regular que haga esto sin fallar si la parte coincidente se encuentra al comienzo de la cadena. Las respuestas negativas parecen ser la única respuesta, pero JavaScript no tiene una.
EDITAR: Esta es la expresión regular que me gustaría trabajar, pero no:
(?<!([abcdefg]))m
Por lo tanto, coincidiría con la 'm' en 'jim' o 'm', pero no con 'jam'
javascript
regex
negative-lookbehind
Andrew Ensley
fuente
fuente
(?:[^abcdefg]|^)(m)
? Al igual que en"mango".match(/(?:[^abcdefg]|^)(m)/)[1]
Respuestas:
Las afirmaciones retrospectivas fueron aceptadas en la especificación ECMAScript en 2018.
Uso positivo retrospectivo:
Uso negativo de lookbehind:
Soporte de plataforma:
fuente
Desde 2018, las afirmaciones de Lookbehind son parte de la especificación del lenguaje ECMAScript .
Respuesta pre-2018
Como Javascript admite la búsqueda anticipada negativa , una forma de hacerlo es:
invertir la cadena de entrada
coincidir con una expresión regular invertida
revertir y reformatear los partidos
Ejemplo 1:
Siguiendo la pregunta de @andrew-ensley:
Salidas:
Ejemplo 2
Siguiendo el comentario de @neaumusic (coincide
max-height
pero noline-height
, siendo el tokenheight
):Salidas:
fuente
max-height
pero noline-height
y solo quiero que el partido seaheight
''(?!\()
reemplazará los apóstrofos''(''test'''''''test
desde el otro extremo, dejando así en(''test'NNNtest
lugar de(''testNNN'test
.Supongamos que desea encontrar todo lo que
int
no esté precedido porunsigned
:Con soporte para mirar hacia atrás negativo:
Sin soporte para la retrospectiva negativa:
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:
se traduciría a:
Es posible que deba jugar con grupos de captura para encontrar el lugar exacto de la cadena que le interesa o si desea reemplazar una parte específica con otra.
fuente
"So it would match the 'm' in 'jim' or 'm', but not 'jam'".replace(/(j(?!([abcdefg])).|^)m/g, "$1[MATCH]")
vuelve ¡"So it would match the 'm' in 'ji[MATCH]' or 'm', but not 'jam'"
Es bastante simple y funciona!La estrategia de Mijoja funciona para su caso específico, pero no en general:
Aquí hay un ejemplo donde el objetivo es hacer coincidir un doble-l pero no si está precedido por "ba". Tenga en cuenta la palabra "balll": la verdadera mirada atrás debería haber suprimido los primeros 2 l, pero coincidió con el segundo par. Pero al hacer coincidir los primeros 2 l y luego ignorar esa coincidencia como falso positivo, el motor de expresiones regulares procede desde el final de esa coincidencia e ignora cualquier carácter dentro del falso positivo.
fuente
Utilizar
fuente
newString
siempre será igualstring
. ¿Por qué tantos votos a favor?"Jim Jam Momm m".replace(/([abcdefg])?m/g, function($0, $1){ return $1 ? $0 : '[match]'; });
. Debería volverJi[match] Jam Mo[match][match] [match]
. Pero también tenga en cuenta que, como Jason menciona a continuación, puede fallar en ciertos casos extremos.Puede definir un grupo que no sea de captura negando su conjunto de caracteres:
... que coincidiría con todos los
m
NO precedidos por cualquiera de esas letras.fuente
(?:[^a-g]|^)m
. Consulte regex101.com/r/jL1iW6/2 para ver un ejemplo en ejecución.Así es como lo logré
str.split(/(?<!^)@/)
para Node.js 8 (que no admite mirar hacia atrás):¿Trabajos? Sí (unicode no probado). ¿Desagradable? Si.
fuente
siguiendo la idea de Mijoja, y aprovechando los problemas expuestos por JasonS, tuve esta idea; Lo revisé un poco, pero no estoy seguro de mí mismo, por lo que una verificación realizada por alguien más experto que yo en js regex sería genial :)
mi salida personal:
El principio es llamar
checker
en cada punto de la cadena entre dos caracteres, siempre que esa posición sea el punto de partida de:--- cualquier subcadena del tamaño de lo que no se desea (aquí
'ba'
, por lo tanto..
) (si se conoce ese tamaño; de lo contrario, quizás sea más difícil de hacer)--- --- o más pequeño que eso si es el comienzo de la cadena:
^.?
y, después de esto,
--- lo que se debe buscar realmente (aquí
'll'
).En cada llamada de
checker
, habrá una prueba para verificar si el valor anteriorll
no es el que no queremos (!== 'ba'
); si ese es el caso, llamamos a otra función, y tendrá que ser esta (doer
) la que hará los cambios en str, si el propósito es este, o más genéricamente, ingresará los datos necesarios para procesar manualmente los resultados del escaneo destr
.aquí cambiamos la cadena por lo que necesitábamos mantener un rastro de la diferencia de longitud para compensar las ubicaciones dadas por
replace
, todas calculadasstr
, que en sí mismas nunca cambian.Como las cadenas primitivas son inmutables, podríamos haber usado la variable
str
para almacenar el resultado de toda la operación, pero pensé que el ejemplo, ya complicado por los reemplazos, sería más claro con otra variable (str_done
).Supongo que, en términos de rendimiento, debe ser bastante duro: todos esos reemplazos inútiles de '' en '',
this str.length-1
veces, más el reemplazo manual por hacedor, lo que significa una gran cantidad de cortes ... probablemente en este caso específico anterior que podría agrupar, cortando la cuerda solo una vez en pedazos alrededor de donde queremos insertarla[match]
e.join()
ingiriéndola consigo[match]
misma.La otra cosa es que no sé cómo manejaría casos más complejos, es decir, valores complejos para la falsa mirada atrás ... la longitud es quizás la información más problemática para obtener.
y, en
checker
caso de múltiples posibilidades de valores no deseados para $ atrás, tendremos que hacer una prueba con otra expresión regular (lo mejor es almacenar en caché (crear) en el exteriorchecker
, para evitar que se cree el mismo objeto de expresión regular) en cada llamada parachecker
) saber si es o no lo que buscamos evitar.espero haber sido claro; si no, no lo dudes, lo intentaré mejor. :)
fuente
Usando su caso, si desea reemplazar
m
con algo, por ejemplo, convertirlo a mayúsculasM
, puede negar el conjunto en el grupo de captura.emparejar
([^a-g])m
, reemplazar con$1M
([^a-g])
coincidirá con cualquier char not (^
) dentro dela-g
rango y lo almacenará en el primer grupo de captura, para que pueda acceder a él con$1
.Así encontramos
im
enjim
y reemplazarlo coniM
lo que se traduce enjiM
.fuente
Como se mencionó anteriormente, JavaScript permite mirar atrás ahora. En navegadores antiguos aún necesita una solución alternativa.
Apuesto a que no hay forma de encontrar una expresión regular sin mirar atrás que ofrezca el resultado exacto. Todo lo que puedes hacer es trabajar con grupos. Supongamos que tiene una expresión regular
(?<!Before)Wanted
, dondeWanted
es la expresión regular que desea hacer coincidir yBefore
es la expresión regular que cuenta lo que no debe preceder a la coincidencia. Lo mejor que puede hacer es negar la expresión regularBefore
y usar la expresión regularNotBefore(Wanted)
. El resultado deseado es el primer grupo$1
.En su caso,
Before=[abcdefg]
que es fácil de negarNotBefore=[^abcdefg]
. Así sería la expresión regular[^abcdefg](m)
. Si necesita la posición deWanted
, también debe agruparNotBefore
, para que el resultado deseado sea el segundo grupo.Si las coincidencias del
Before
patrón tienen una longitud fijan
, es decir, si el patrón no contiene tokens repetitivos, puede evitar negar elBefore
patrón y usar la expresión regular(?!Before).{n}(Wanted)
, pero aún así tiene que usar el primer grupo o usar la expresión regular(?!Before)(.{n})(Wanted)
y usar el segundo grupo. En este ejemplo, el patrónBefore
en realidad tiene una longitud fija, es decir, 1, a fin de utilizar la expresión regular(?![abcdefg]).(m)
o(?![abcdefg])(.)(m)
. Si está interesado en todas las coincidencias, agregue lag
bandera, vea mi fragmento de código:fuente
Esto efectivamente lo hace
Buscar y reemplazar ejemplo
Tenga en cuenta que la cadena de búsqueda negativa debe tener 1 carácter para que esto funcione.
fuente
"m".match(/[^a-g]m/)
yeildsnull
también. Quiero la "m" en ese caso también./(?![abcdefg])[^abcdefg]m/gi
Sí, esto es un truco.fuente
(?![abcdefg])
es totalmente redundante,[^abcdefg]
ya que hace su trabajo para evitar que esos caracteres coincidan.