Retornar posições de uma correspondência de expressão regular () em Javascript?

154

Existe uma maneira de recuperar as posições de caractere (inicial) dentro de uma sequência de resultados de uma correspondência de expressão regular () em Javascript?

stagas
fonte

Respostas:

225

execretorna um objeto com uma indexpropriedade:

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

E para várias correspondências:

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

quiabo
fonte
5
Obrigado pela ajuda! Você pode me dizer também como encontro os índices de várias correspondências?
stagas
9
Nota: o uso de recomo variável e a adição do gmodificador são cruciais! Caso contrário, você obterá um loop infinito.
oriadam
1
@ OnurYıldırım - aqui está um jsFiddle dele trabalhando ... Eu testei todo o caminho de volta para IE5 ... funciona muito bem: jsfiddle.net/6uwn1vof
Jimbo Jonny
1
@JimboJonny, hm bem, eu aprendi algo novo. Meu caso de teste retorna undefined. jsfiddle.net/6uwn1vof/2, que não é um exemplo de pesquisa como o seu.
Onur Yıldırım
1
@ OnurYıldırım - Remova a gbandeira e ela funcionará. Como matché uma função da string, não da regex, ela não pode ser com estado exec, portanto, ela só a trata como exec(por exemplo, tem uma propriedade de índice) se você não está procurando uma correspondência global ... porque a condição de estado não importa .
Jimbo Jonny
60

Aqui está o que eu vim com:

// 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
fonte
18
match.index + match[0].lengthtambém funciona para a posição final.
Beni Cherniavsky-Paskin
muito bom - comparação feita aqui
Louis Maddox
1
@ BeniCherniavsky-Paskin, não seria a posição final match.index + match[0].length - 1?
David
1
@ David, eu quis dizer posição final exclusiva, como por exemplo, por .slice()e .substring(). O fim inclusivo seria 1 a menos, como você diz. (Tenha cuidado para que inclusivo geralmente signifique o índice do último caractere dentro da correspondência, a menos que seja uma correspondência vazia onde é 1 antes da correspondência e pode estar -1fora da cadeia inteiramente para correspondência vazia no início ...)
Beni Cherniavsky-Paskin
16

Nos documentos developer.mozilla.org sobre o .match()método String :

A matriz retornada possui uma propriedade de entrada extra, que contém a string original que foi analisada. Além disso, possui uma propriedade index, que representa o índice baseado em zero da correspondência na string .

Ao lidar com uma regex não global (ou seja, sem gsinalização em sua regex), o valor retornado por .match()possui uma indexpropriedade ... tudo o que você precisa fazer é acessá-la.

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

Aqui está um exemplo mostrando que também funciona:

var str = 'my string here';

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

alert(index); // <- 10

Testei com sucesso isso desde o IE5.

Jimbo Jonny
fonte
6

Você pode usar o searchmétodo do Stringobjeto. Isso funcionará apenas para a primeira partida, mas fará o que você descreve. Por exemplo:

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

Aqui está um recurso interessante que descobri recentemente, tentei isso no console e parece funcionar:

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

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

Que retornou: "borda 6 inferior 13 esquerda 18 raio"

Então, isso parece ser o que você está procurando.

felipeab
fonte
6
apenas lembre-se de que as funções de substituição também adicionam grupos de captura, portanto, é sempre a penúltima entrada na função de substituição argumentsque é a posição. Não é "o segundo argumento". Os argumentos da função são "correspondência completa, grupo1, grupo2, ...., índice de correspondência, sequência completa correspondida"
Mike 'Pomax' Kamermans
1

Esse membro fn retorna uma matriz de posições baseadas em 0, se houver, da palavra de entrada dentro do 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 ;
}

Agora tente

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 ) );

Você também pode inserir expressões regulares:

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

Aqui se obtém o índice de posição do termo linear.

Sandro Rosa
fonte
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
fonte
Isto está incorreto. str.indexOfaqui apenas encontra a próxima ocorrência do texto capturado pela correspondência, que não é necessariamente a correspondência. O JS regex suporta condições no texto fora da captura com lookahead. Por exemplo, searchIndex("foobarfoobaz", "foo(?=baz)", true)deve dar [6], não [0].
precisa saber é
por isso que `[] .forEach.call (arr, função (elemento)` porque não arr.forEach ou arr.map
Ankit Kumar
1

Nos navegadores modernos, você pode fazer isso com string.matchAll () .

O benefício dessa abordagem vs RegExp.exec()é que ela não depende do estado regular, como na resposta da @ Gumbo .

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

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

brismuth
fonte
-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

ou

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
fonte