Zwrócić pozycje dopasowania wyrażenia regularnego () w JavaScript?

154

Czy istnieje sposób na pobranie (początkowych) pozycji znaków w ciągu wyników dopasowania wyrażenia regularnego () w JavaScript?

stagi
źródło

Odpowiedzi:

225

execzwraca obiekt z indexwłaściwością:

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

Oraz dla wielu dopasowań:

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

Gumbo
źródło
5
Dzięki za pomoc! Czy możesz mi też powiedzieć, jak znaleźć indeksy wielu dopasowań?
Stagas
9
Uwaga: użycie rezmiennej jako zmiennej i dodanie gmodyfikatora są kluczowe! W przeciwnym razie otrzymasz niekończącą się pętlę.
oriadam
1
@ OnurYıldırım - oto jak działa jsfiddle ... Przetestowałem to aż do IE5 ... działa świetnie: jsfiddle.net/6uwn1vof
Jimbo Jonny
1
@JimboJonny, hm cóż, nauczyłem się czegoś nowego. Mój przypadek testowy powraca undefined. jsfiddle.net/6uwn1vof/2, który nie jest przykładem podobnym do wyszukiwania, jak twój.
Onur Yıldırım
1
@ OnurYıldırım - Usuń gflagę i zadziała. Ponieważ matchjest funkcją ciągu, a nie wyrażenia regularnego, nie może być stanową exec, więc traktuje ją tylko jako exec(tj. Ma właściwość index), jeśli nie szukasz globalnego dopasowania ... ponieważ wtedy stan nie ma znaczenia .
Jimbo Jonny
60

Oto, co wymyśliłem:

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

stagi
źródło
18
match.index + match[0].lengthdziała również dla pozycji końcowej.
Beni Cherniavsky-Paskin
naprawdę fajne - porównanie przytoczone tutaj
Louis Maddox
1
@ BeniCherniavsky-Paskin, czy nie byłaby to pozycja końcowa match.index + match[0].length - 1?
David
1
@David, miałem na myśli ekskluzywną pozycję końcową, jak np. Przez .slice()i .substring(). Koniec obejmujący byłby o 1 mniej, jak mówisz. (Uważaj, że dołączenie zwykle oznacza indeks ostatniego znaku wewnątrz dopasowania, chyba że jest to pusty mecz, w którym jest 1 przed dopasowaniem i może znajdować -1się całkowicie poza ciągiem dla pustego dopasowania na początku ...)
Beni Cherniavsky-Paskin
16

Z dokumentacji developer.mozilla.org na temat .match()metody String :

Zwrócona Array ma dodatkową właściwość wejściową, która zawiera oryginalny ciąg, który został przeanalizowany. Ponadto ma właściwość index, która reprezentuje liczony od zera indeks dopasowania w ciągu .

Kiedy mamy do czynienia z nieglobalnym wyrażeniem regularnym (tj. Brakiem gflagi w Twoim wyrażeniu regularnym), wartość zwracana przez .match()ma indexwłaściwość… wszystko, co musisz zrobić, to uzyskać do niego dostęp.

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

Oto przykład pokazujący, że również działa:

var str = 'my string here';

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

alert(index); // <- 10

Z powodzeniem przetestowałem to aż do IE5.

Jimbo Jonny
źródło
6

Możesz użyć searchmetody Stringobiektu. To zadziała tylko dla pierwszego dopasowania, ale poza tym zrobi to, co opisujesz. Na przykład:

"How are you?".search(/are/);
// 4
Jimmy Cuadra
źródło
6

Oto fajna funkcja, którą niedawno odkryłem, wypróbowałem to na konsoli i wydaje się, że działa:

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

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

Który zwrócił: „border 6 bottom 13 left 18 radius”

Więc wydaje się, że to jest to, czego szukasz.

felipeab
źródło
6
uważaj tylko, że funkcje zastępujące również dodają grupy przechwytywania, więc pamiętaj, że zawsze jest to pozycja przedostatnia w funkcji zamiany arguments. Nie „drugi argument”. Argumentami funkcji są "pełne dopasowanie, grupa1, grupa2, ...., indeks dopasowania, pełny ciąg dopasowany do"
Mike 'Pomax' Kamermans
2

W nowoczesnych przeglądarkach można to osiągnąć za pomocą string.matchAll () .

Zaletą tego podejścia RegExp.exec()jest to, że nie polega ono na tym, że wyrażenie regularne jest stanowe, jak w odpowiedzi @ Gumbo .

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

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

brizmut
źródło
1

Ten element członkowski fn zwraca tablicę pozycji opartych na 0, jeśli takie istnieją, słowa wejściowego wewnątrz obiektu 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 ;
}

Spróbuj teraz

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

Możesz także wprowadzić wyrażenia regularne:

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

Tutaj otrzymujemy indeks pozycji członu liniowego.

Sandro Rosa
źródło
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));
Jarosław
źródło
To jest niepoprawne. str.indexOftutaj po prostu znajduje następne wystąpienie tekstu przechwyconego przez dopasowanie, które niekoniecznie jest zgodne. JS regex obsługuje warunki w tekście poza przechwytywaniem z wyprzedzeniem. Na przykład searchIndex("foobarfoobaz", "foo(?=baz)", true)powinien dawać [6], a nie [0].
rakslice
dlaczego `[] .forEach.call (arr, function (element)` dlaczego nie arr.forEach lub arr.map
Ankit Kumar
-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

lub

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
źródło