Jak przechwycić dowolną liczbę grup w JavaScript Regexp?

84

Spodziewałbym się tej linii JavaScript:

"foo bar baz".match(/^(\s*\w+)+$/)

aby zwrócić coś takiego:

["foo bar baz", "foo", " bar", " baz"]

ale zamiast tego zwraca tylko ostatnie przechwycone dopasowanie:

["foo bar baz", " baz"]

Czy jest sposób na zdobycie wszystkich przechwyconych meczów?

disc0dancer
źródło

Odpowiedzi:

93

Gdy powtarzasz grupę przechwytywania, w większości smaków zachowywane jest tylko ostatnie przechwycenie; wszelkie poprzednie przechwycenia są nadpisywane. W niektórych smakach, np. .NET, można uzyskać wszystkie pośrednie przechwytywania, ale nie jest to w przypadku Javascript.

Oznacza to, że w Javascript, jeśli masz wzorzec z N grup przechwytywania, możesz przechwycić tylko dokładnie N ciągów na dopasowanie, nawet jeśli niektóre z tych grup zostały powtórzone.

Więc ogólnie rzecz biorąc, w zależności od tego, co musisz zrobić:

  • Jeśli jest to opcja, zamiast tego podziel na ograniczniki
  • Zamiast dopasowywania /(pattern)+/, może dopasowywania /pattern/g, być może w execpętli
    • Zwróć uwagę, że te dwa nie są dokładnie równoważne, ale może to być opcja
  • Wykonaj dopasowanie wielopoziomowe:
    • Przechwyć powtarzającą się grupę w jednym meczu
    • Następnie uruchom kolejne wyrażenie regularne, aby rozdzielić to dopasowanie

Bibliografia


Przykład

Oto przykład dopasowania <some;words;here>w tekście, użycia execpętli, a następnie podziału w ;celu uzyskania pojedynczych słów ( zobacz także na ideone.com ):

var text = "a;b;<c;d;e;f>;g;h;i;<no no no>;j;k;<xx;yy;zz>";

var r = /<(\w+(;\w+)*)>/g;

var match;
while ((match = r.exec(text)) != null) {
  print(match[1].split(";"));
}
// c,d,e,f
// xx,yy,zz

Zastosowany wzór to:

      _2__
     /    \
<(\w+(;\w+)*)>
 \__________/
      1

Dopasowuje <word>, <word;another>, <word;another;please>, itd. Grupa 2 powtarza się uchwycić każdą liczbę słów, ale może tylko zachować ostatnią przechwytywanie. Cała lista słów jest przechwytywana przez grupę 1; ten ciąg jest następnie splitna separatorze średnika.

Powiązane pytania

smary wielogenowe
źródło
7

A co powiesz na to? "foo bar baz".match(/(\w+)+/g)

meder omuraliev
źródło
Twój kod działa, ale dodanie flagi globalnej do mojego przykładu nie rozwiąże problemu: "foo bar baz" .match (/ ^ (\ s * \ w +) + $ / g) zwróci ["foo bar baz"]
disc0dancer
zadziała, jeśli zmienisz je na poniższe wyrażenie regularne @ Jet. "foo bar baz".match(/\w+/g) //=> ["foo", "bar", "baz"]. ignoruje dopasowany ciąg z przodu, ale nadal jest rozsądną alternatywą.
Jed Schneider
6

Jeśli nie masz bardziej skomplikowanych wymagań dotyczących sposobu dzielenia ciągów, możesz je podzielić, a następnie zwrócić z nimi początkowy ciąg:

var data = "foo bar baz";
var pieces = data.split(' ');
pieces.unshift(data);
gddc
źródło
1
Skończyło się na tym, że była to tylko rada, której potrzebowałem, aby obudzić mnie na fakt, że przynajmniej w mojej obecnej aplikacji nie potrzebowałem niczego bardziej wyrafinowanego niż split ().
Hefajstos
4

spróbuj użyć „g”:

"foo bar baz".match(/\w+/g)
Strumień
źródło