17º diciembre 2020

Indicador adhesivo “y”, buscando en una posición.

EL indicador y permite realizar la búsqueda en una posición dada en el string de origen.

Para entender el caso de uso del indicador y, y ver lo notable que es, exploremos un ejemplo práctico.

Una tarea común para regexps es el “Análisis léxico”: tenemos un texto, por ej. en un lenguaje de programación, y analiza sus elementos estructurales.

Por ejemplo, HTML tiene etiquetas y atributos, el código JavaScript tiene funciones, variables, etc.

Escribir analizadores léxicos es un área especial, con sus propias herramientas y algoritmos, así que no profundizaremos en ello; pero existe una tarea común: leer algo en una posición dada.

Por ej. tenemos una cadena de código let varName = "value", y necesitamos leer el nombre de su variable, que comienza en la posición 4.

Buscaremos el nombre de la variable usando regexp \w+. En realidad, el nombre de la variable de JavaScript necesita un regexp un poco más complejo para un emparejamiento más preciso, pero aquí eso no importa.

Una llamada a str.match(/\w+/) solo encontrará la primera palabra de la línea, o todas las palabras con el indicador g. Pero solo necesitamos una palabra en la posición 4.

Para buscar desde la posición dada, usamos el método regexp.exec(str).

regexp no tiene indicadores g o y, entonces este método busca la primera coincidencia en el string str, exactamente como str.match(regexp). Un caso tan simple sin indicadores no nos interesa aquí.

Si existe el indicador g, realiza la búsqueda en el string str empezando desde la posición almacenada en su propiedad regexp.lastIndex. Y si encuentra una coincidencia, establece regexp.lastIndex en el index inmediatamente después del emparejamiento.

Cuando un regex es creado, su lastIndex es 0.

Entonces, llamadas sucesivas a regexp.exec(str) devuelve coincidencias una después de la otra.

Un ejemplo (con el indicador g):

let str = 'let varName';

let regexp = /\w+/g;
alert(regexp.lastIndex); // 0 (inicialmente lastIndex=0)

let word1 = regexp.exec(str);
alert(word1[0]); // let (primera palabra)
alert(regexp.lastIndex); // 3 (Posición posterior al emparejamiento)

let word2 = regexp.exec(str);
alert(word2[0]); // varName (2da palabra)
alert(regexp.lastIndex); // 11 (Posición posterior al emparejamiento)

let word3 = regexp.exec(str);
alert(word3); // null (no más emparejamientos)
alert(regexp.lastIndex); // 0 (reinicia en el final de la búsqueda)

Cada coincidencia es devuelta como un array con grupos y propiedades adicionales.

Podemos conseguir todas las coincidencias en el loop:

let str = 'let varName';
let regexp = /\w+/g;

let result;

while (result = regexp.exec(str)) {
  alert( `Found ${result[0]} at position ${result.index}` );
  // Found let at position 0, then
  // Found varName at position 4
}

Tal uso de regexp.exec es una alternativa al método str.match bAll.

A diferencia de otros métodos, podemos establecer nuestro propio lastIndex, para comenzar la búsqueda desde la posición dada.

Por ejemplo, encontremos una palabra, comenzando desde la posición 4:

let str = 'let varName = "value"';

let regexp = /\w+/g; // Sin el indicador “g”, la propiedad lastindex es ignorada.

regexp.lastIndex = 4;

let word = regexp.exec(str);
alert(word); // varName

Realizamos una búsqueda de \w+, comenzando desde la posición regexp.lastIndex = 4.

Nota que la búsqueda comienza en la posición lastIndex y luego sigue adelante. Si no hay ninguna palabra en la posición lastIndex pero la hay en algún lugar posterior, entonces será encontrada:

let str = 'let varName = "value"';

let regexp = /\w+/g;

regexp.lastIndex = 3;

let word = regexp.exec(str);
alert(word[0]); // varName
alert(word.index); // 4

…Así que, con la propiedad lastIndex del indicador g se establece la posición inicial de la búsqueda.

El indicador y hace que regexp.exec busque exactamente en la posición lastIndex, ni antes ni después.

Aquí está la misma búsqueda con el indicador y:

let str = 'let varName = "value"';

let regexp = /\w+/y;

regexp.lastIndex = 3;
alert( regexp.exec(str) ); // null (Hay un espacio en la posición 3, no una palabra)

regexp.lastIndex = 4;
alert( regexp.exec(str) ); // varName (Una palabra en la posición 4)

Como podemos ver, el /\w+/y de regexp no coincide en la posición 3 (a diferencia del indicador g), pero coincide en la posición 4.

Imagina que tenemos un texto largo, y no hay coincidencias en él. Entonces la búsqueda con el indicador g irá hasta el final del texto, y esto tomará significativamente más tiempo que la búsqueda con el indicador y.

En tareas tales como el análisis léxico, normalmente hay muchas búsquedas en una posición exacta. Usar el indicador y es la clave para un buen desempeño.

Mapa del Tutorial

Comentarios

lea esto antes de comentar…
  • Si tiene sugerencias sobre qué mejorar, por favor enviar una propuesta de GitHub o una solicitud de extracción en lugar de comentar.
  • Si no puede entender algo en el artículo, por favor explique.
  • Para insertar algunas palabras de código, use la etiqueta <code>, para varias líneas – envolverlas en la etiqueta <pre>, para más de 10 líneas – utilice una entorno controlado (sandbox) (plnkr, jsbin, codepen…)