¿Devolver posiciones de una expresión regular () en Javascript?

154

¿Hay alguna manera de recuperar las posiciones de caracteres (iniciales) dentro de una cadena de los resultados de una coincidencia de expresiones regulares () en Javascript?

stagas
fuente

Respuestas:

225

execdevuelve un objeto con una indexpropiedad:

var match = /bar/.exec("foobar");
if (match) {
    console.log("match found at " + match.index);
}

Y para múltiples partidos:

var re = /bar/g,
    str = "foobarfoobar";
while ((match = re.exec(str)) != null) {
    console.log("match found at " + match.index);
}

Gumbo
fuente
55
¡Gracias por tu ayuda! ¿Me puede decir también cómo encuentro los índices de múltiples coincidencias?
stagas
9
Nota: ¡usar el recomo variable y agregar el gmodificador son cruciales! De lo contrario, obtendrá un bucle sin fin.
oriadam
1
@ OnurYıldırım: aquí hay un jsfiddle que funciona ... Lo he probado todo el camino hasta IE5 ... funciona muy bien: jsfiddle.net/6uwn1vof
Jimbo Jonny
1
@ JimboJonny, hm bueno, aprendí algo nuevo. Mi caso de prueba regresa undefined. jsfiddle.net/6uwn1vof/2 que no es un ejemplo de búsqueda como el tuyo.
Onur Yıldırım
1
@ OnurYıldırım: elimine la gbandera y funcionará. Dado que matches una función de la cadena, no la expresión regular, no puede ser con estado exec, por lo que solo la trata como exec(es decir, tiene una propiedad de índice) si no está buscando una coincidencia global ... porque entonces la condición de estado no importa .
Jimbo Jonny
60

Esto es lo que se me ocurrió:

// Finds starting and ending positions of quoted text
// in double or single quotes with escape char support like \" \'
var str = "this is a \"quoted\" string as you can 'read'";

var patt = /'((?:\\.|[^'])*)'|"((?:\\.|[^"])*)"/igm;

while (match = patt.exec(str)) {
  console.log(match.index + ' ' + patt.lastIndex);
}

stagas
fuente
18
match.index + match[0].lengthTambién funciona para la posición final.
Beni Cherniavsky-Paskin
realmente agradable - la comparación es esencial aquí
Louis Maddox
1
@ BeniCherniavsky-Paskin, ¿no sería la posición final match.index + match[0].length - 1?
David
1
@David, me refería a una posición final exclusiva, tomada, por ejemplo, por .slice()y .substring(). El final inclusivo sería 1 menos como tú dices. (Tenga cuidado de que inclusivo generalmente signifique el índice del último personaje dentro del partido, a menos que sea un partido vacío donde es 1 antes del partido y podría estar completamente -1fuera de la cadena para el partido vacío al inicio ...)
Beni Cherniavsky-Paskin
16

De los documentos de developer.mozilla.org sobre el .match()método String :

La matriz devuelta tiene una propiedad de entrada adicional, que contiene la cadena original que se analizó. Además, tiene una propiedad de índice, que representa el índice basado en cero de la coincidencia en la cadena .

Cuando se trata de una expresión regular no global (es decir, sin gmarca en su expresión regular), el valor devuelto por .match()tiene una indexpropiedad ... todo lo que tiene que hacer es acceder a ella.

var index = str.match(/regex/).index;

Aquí hay un ejemplo que muestra que también funciona:

var str = 'my string here';

var index = str.match(/here/).index;

alert(index); // <- 10

He probado con éxito todo esto desde IE5.

Jimbo Jonny
fuente
6

Puedes usar el searchmétodo del Stringobjeto. Esto solo funcionará para el primer partido, pero de lo contrario hará lo que describas. Por ejemplo:

"How are you?".search(/are/);
// 4
Jimmy Cuadra
fuente
6

Aquí hay una característica genial que descubrí recientemente, probé esto en la consola y parece funcionar:

var text = "border-bottom-left-radius";

var newText = text.replace(/-/g,function(match, index){
    return " " + index + " ";
});

Que volvió: "borde 6 inferior 13 izquierda 18 radio"

Así que esto parece ser lo que estás buscando.

felipeab
fuente
66
solo tenga en cuenta que las funciones de reemplazo también agregan grupos de captura, así que tenga en cuenta que siempre es la penúltima entrada en la función de reemplazo argumentsesa es la posición. No "el segundo argumento". Los argumentos de la función son "coincidencia completa, grupo1, grupo2, ...., índice de coincidencia, cadena completa comparada con"
Mike 'Pomax' Kamermans
2

En los navegadores modernos, puede lograr esto con string.matchAll () .

El beneficio de este enfoque vs RegExp.exec()es que no se basa en que la expresión regular sea con estado, como en la respuesta de @ Gumbo .

let regexp = /bar/g;
let str = 'foobarfoobar';

let matches = [...str.matchAll(regexp)];
matches.forEach((match) => {
    console.log("match found at " + match.index);
});

brismuth
fuente
1

Este miembro fn devuelve una matriz de posiciones basadas en 0, si las hay, de la palabra de entrada dentro del objeto String

String.prototype.matching_positions = function( _word, _case_sensitive, _whole_words, _multiline )
{
   /*besides '_word' param, others are flags (0|1)*/
   var _match_pattern = "g"+(_case_sensitive?"i":"")+(_multiline?"m":"") ;
   var _bound = _whole_words ? "\\b" : "" ;
   var _re = new RegExp( _bound+_word+_bound, _match_pattern );
   var _pos = [], _chunk, _index = 0 ;

   while( true )
   {
      _chunk = _re.exec( this ) ;
      if ( _chunk == null ) break ;
      _pos.push( _chunk['index'] ) ;
      _re.lastIndex = _chunk['index']+1 ;
   }

   return _pos ;
}

Ahora intenta

var _sentence = "What do doers want ? What do doers need ?" ;
var _word = "do" ;
console.log( _sentence.matching_positions( _word, 1, 0, 0 ) );
console.log( _sentence.matching_positions( _word, 1, 1, 0 ) );

También puede ingresar expresiones regulares:

var _second = "z^2+2z-1" ;
console.log( _second.matching_positions( "[0-9]\z+", 0, 0, 0 ) );

Aquí se obtiene el índice de posición del término lineal.

Sandro Rosa
fuente
1
var str = "The rain in SPAIN stays mainly in the plain";

function searchIndex(str, searchValue, isCaseSensitive) {
  var modifiers = isCaseSensitive ? 'gi' : 'g';
  var regExpValue = new RegExp(searchValue, modifiers);
  var matches = [];
  var startIndex = 0;
  var arr = str.match(regExpValue);

  [].forEach.call(arr, function(element) {
    startIndex = str.indexOf(element, startIndex);
    matches.push(startIndex++);
  });

  return matches;
}

console.log(searchIndex(str, 'ain', true));
Yaroslav
fuente
Esto es incorrecto. str.indexOfaquí solo se encuentra la siguiente aparición del texto capturado por la coincidencia, que no es necesariamente la coincidencia. JS regex admite condiciones en el texto fuera de la captura con anticipación. Por ejemplo searchIndex("foobarfoobaz", "foo(?=baz)", true)debería dar [6], no [0].
rakslice
por qué `[] .forEach.call (arr, function (element)` por qué no arr.forEach o arr.map
Ankit Kumar el
-1
function trimRegex(str, regex){
    return str.substr(str.match(regex).index).split('').reverse().join('').substr(str.match(regex).index).split('').reverse().join('');
}

let test = '||ab||cd||';
trimRegex(test, /[^|]/);
console.log(test); //output: ab||cd

o

function trimChar(str, trim, req){
    let regex = new RegExp('[^'+trim+']');
    return str.substr(str.match(regex).index).split('').reverse().join('').substr(str.match(regex).index).split('').reverse().join('');
}

let test = '||ab||cd||';
trimChar(test, '|');
console.log(test); //output: ab||cd
SwiftNinjaPro
fuente