javascript regex regex-lookarounds

La expresión regex negativa negativa no es válida en JavaScript



regex-lookarounds (2)

Aquí hay una manera de analizar cadenas HTML usando DOM en JS y realizar reemplazos solo fuera de las etiquetas:

var s = ''<span class="css">55</span> 2 >= 1 2 > 1''; var doc = document.createDocumentFragment(); var wrapper = document.createElement(''myelt''); wrapper.innerHTML = s; doc.appendChild( wrapper ); function textNodesUnder(el){ var n, walk=document.createTreeWalker(el,NodeFilter.SHOW_TEXT,null,false); while(n=walk.nextNode()) { if (n.parentNode.nodeName.toLowerCase() === ''myelt'') n.nodeValue = n.nodeValue.replace(/>=?/g, "EQUAL"); } return el.firstChild.innerHTML; } var res = textNodesUnder(doc); console.log(res); alert(res);

Esta pregunta ya tiene una respuesta aquí:

Considerar:

var re = /(?<=foo)bar/gi;

Es una expresión regular no válida en Plunker. ¿Por qué?


JavaScript carece de soporte para lookbehinds hacia lookbehinds como (?<=…) (positivo) y (?<!…) (negativo), pero eso no significa que aún no pueda implementar este tipo de lógica en JavaScript.

Coincidencia (no global)

Partido positivo de lookbehind:

// from /(?<=foo)bar/i var matcher = mystring.match( /foo(bar)/i ); if (matcher) { // do stuff with matcher[1] which is the part that matches "bar" }

Ancho fijo de lookbehind negativo:

// from /(?<!foo)bar/i var matcher = mystring.match( /(?!foo)(?:^.{0,2}|.{3})(bar)/i ); if (matcher) { // do stuff with matcher[1] ("bar"), knowing that it does not follow "foo" }

Las retrospectivas negativas se pueden hacer sin la bandera global, pero solo con un ancho fijo, y debe calcular ese ancho (que puede ser difícil con las alternations ). Usar (?!foo).{3}(bar) sería más simple y más o menos equivalente, pero desde entonces no coincidirá con una línea que comience con "armadura" . no puede coincidir con las nuevas líneas, por lo que necesitamos la alternancia del código anterior para que coincida con las líneas con "barra" antes del carácter cuatro.

Si lo necesita con un ancho variable, use la siguiente solución global y ponga un break al final de la estrofa if . (Esta limitación es bastante común. .NET , vim y JGsoft son los only motores de JGsoft regulares que admiten ancho de retrospectiva variable. PCRE , PHP y Perl están limitados a ancho fijo. Python requiere un módulo de expresiones regulares alternativo para admitir esto. Dicho esto, la lógica de la solución a continuación debería funcionar para todos los idiomas que admiten expresiones regulares).

Coincidencia (global)

Cuando necesita hacer un bucle en cada coincidencia en una cadena dada (el modificador g , coincidencia global), debe redefinir la variable de matcher en cada iteración del bucle y debe usar RegExp.exec() (con el RegExp creado antes del bucle ) ¡porque String.match() interpreta el modificador global de manera differently y creará un bucle infinito!

Mirada positiva global detrás:

var re = /foo(bar)/gi; // from /(?<=foo)bar/gi while ( matcher = re.exec(mystring) ) { // do stuff with matcher[1] which is the part that matches "bar" }

"Cosas", por supuesto, puede incluir rellenar una matriz para su uso posterior.

Mirada negativa global:

var re = /(foo)?bar/gi; // from /(?<!foo)bar/gi while ( matcher = re.exec(mystring) ) { if (!matcher[1]) { // do stuff with matcher[0] ("bar"), knowing that it does not follow "foo" } }

Tenga en cuenta que hay cases en los que esto no representará completamente la mirada negativa hacia atrás. Considere /(?<!ba)ll/g haciendo coincidir contra Fall ball bill balll llama . Encontrará solo tres de los cuatro partidos deseados porque cuando analiza balll , encuentra la ball y luego continúa un personaje tarde en l llama . Esto solo ocurre cuando una coincidencia parcial al final podría interferir con una coincidencia parcial en un extremo diferente ( balll breaks (ba)?ll pero foobarbar está bien con (foo)?bar ) La única solución para esto es usar el fijo anterior método de ancho.

Sustitución

Hay un maravilloso artículo llamado Mimicking Lookbehind en JavaScript que describe cómo hacer esto.
Incluso tiene un seguimiento que apunta a una colección de funciones cortas que implementan esto en JS.

Implementar lookbehind en String.replace() es mucho más fácil ya que puede crear una función anónima como reemplazo y manejar la lógica de lookbehind en esa función.

Estos funcionan en la primera coincidencia, pero pueden hacerse globales simplemente agregando el modificador g .

Reemplazo positivo de lookbehind:

// assuming you wanted mystring.replace(/(?<=foo)bar/i, "baz"): mystring = mystring.replace( /(foo)?bar/i, function ($0, $1) { return ($1 ? $1 + "baz" : $0) } );

Esto toma la cadena de destino y reemplaza las instancias de bar con baz siempre que sigan a foo . Si lo hacen, $1 coincide y el operador ternario ( ?: Devuelve el texto coincidente y el texto de reemplazo (pero no la parte de la bar ). De lo contrario, el operador ternario devuelve el texto original.

Reemplazo negativo de lookbehind:

// assuming you wanted mystring.replace(/(?<!foo)bar/i, "baz"): mystring = mystring.replace( /(foo)?bar/i, function ($0, $1) { return ($1 ? $0 : "baz") } );

Esto es esencialmente lo mismo, pero dado que es una mirada negativa hacia atrás, actúa cuando falta $1 (no necesitamos decir $1 + "baz" aquí porque sabemos que $1 está vacío).

Esto tiene la misma advertencia que la otra solución de aspecto negativo de ancho dinámico detrás y se soluciona de manera similar mediante el uso del método de ancho fijo.