Jak uzyskać dostęp do dopasowanych grup w wyrażeniu regularnym JavaScript?

Chcę dopasować część łańcucha za pomocą wyrażenia regularnego , a następnie uzyskać dostęp do tego fragmentu w nawiasie:

var myString = "something format_abc"; // I want "abc"

var arr = /(?:^|\s)format_(.*?)(?:\s|$)/.exec(myString);

console.log(arr);     // Prints: [" format_abc", "abc"] .. so far so good.
console.log(arr[1]);  // Prints: undefined  (???)
console.log(arr[0]);  // Prints: format_undefined (!!!)
Co robię źle?

Odkryłem, że nie ma nic złego w powyższym kodzie wyrażenia regularnego: rzeczywisty ciąg znaków, który testowałem, był taki:

"date format_%A"

Zgłaszanie, że "%A" jest niezdefiniowane wydaje się bardzo dziwnym zachowaniem, ale nie jest bezpośrednio związane z tym pytaniem, więc otworzyłem nowe, dlaczego dopasowany podłańcuch zwraca "undefined" w JavaScript?.


Problem polegał na tym, że console.log pobiera swoje parametry jak polecenie printf, a ponieważ łańcuch, który logowałem ("%A") miał specjalną wartość, próbował znaleźć wartość następnego parametru.

Author: Community, 2009-01-11

13 answers

Możesz uzyskać dostęp do takich grup przechwytywania:

var myString = "something format_abc";
var myRegexp = /(?:^|\s)format_(.*?)(?:\s|$)/g;
var match = myRegexp.exec(myString);
console.log(match[1]); // abc

I jeśli jest wiele dopasowań, możesz je iterować:

var myString = "something format_abc";
var myRegexp = /(?:^|\s)format_(.*?)(?:\s|$)/g;
match = myRegexp.exec(myString);
while (match != null) {
  // matched text: match[0]
  // match start: match.index
  // capturing group n: match[n]
  console.log(match[0])
  match = myRegexp.exec(myString);
}
 1348
Author: CMS,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-08-16 01:42:44

Oto metoda, której możesz użyć, aby uzyskać n TH grupę przechwytywania dla każdego dopasowania:

function getMatches(string, regex, index) {
  index || (index = 1); // default to the first capturing group
  var matches = [];
  var match;
  while (match = regex.exec(string)) {
    matches.push(match[index]);
  }
  return matches;
}


// Example :
var myString = 'something format_abc something format_def something format_ghi';
var myRegEx = /(?:^|\s)format_(.*?)(?:\s|$)/g;

// Get an array containing the first capturing group for every match
var matches = getMatches(myString, myRegEx, 1);

// Log results
document.write(matches.length + ' matches found: ' + JSON.stringify(matches))
console.log(matches);
 156
Author: Mathias Bynens,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-04-17 06:52:57

var myString = "something format_abc";
var arr = myString.match(/\bformat_(.*?)\b/);
console.log(arr[0] + " " + arr[1]);

\b to nie to samo. (Działa na --format_foo/, ale nie działa na format_a_b), ale chciałem pokazać alternatywę dla Twojego wyrażenia, co jest w porządku. Oczywiście ważne jest połączenie.

 48
Author: PhiLho,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 16:08:00

W odniesieniu do powyższych przykładowych nawiasów, Szukałem tutaj odpowiedzi po tym, jak nie dostałem tego, od czego chciałem:

var matches = mystring.match(/(?:neededToMatchButNotWantedInResult)(matchWanted)/igm);

Po spojrzeniu na nieco zawiłe wywołania funkcji z while I .push () powyżej, dotarło do mnie, że problem można rozwiązać bardzo elegancko za pomocą mystring.replace() zamiast tego (Zamiana nie jest celem, a nawet nie jest wykonana, czystą, wbudowaną opcją rekurencyjnego wywołania funkcji dla drugiego parametru jest!):

var yourstring = 'something format_abc something format_def something format_ghi';

var matches = [];
yourstring.replace(/format_([^\s]+)/igm, function(m, p1){ matches.push(p1); } );

Po tego chyba nigdy nie użyję .match()

 22
Author: Alexz,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-07-17 04:53:32

Twoja składnia prawdopodobnie nie jest najlepsza do zachowania. FF / Gecko definiuje RegExp jako rozszerzenie funkcji.
(FF2 poszedł tak daleko jak typeof(/pattern/) == 'function')

Wygląda na to, że jest to specyficzne dla FF -- IE, Opery i Chrome wszystkie rzucają dla niego wyjątki.

Zamiast tego, użyj jednej z metod wymienionych wcześniej przez innych: RegExp#exec lub String#match.
Oferują one takie same wyniki:

var regex = /(?:^|\s)format_(.*?)(?:\s|$)/;
var input = "something format_abc";

regex(input);        //=> [" format_abc", "abc"]
regex.exec(input);   //=> [" format_abc", "abc"]
input.match(regex);  //=> [" format_abc", "abc"]
 15
Author: Jonathan Lonowski,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-01-11 12:55:15

Wreszcie znalazłem jeden kod liniowy, który działał dobrze dla mnie (JS ES6):

var reg = /#([\S]+)/igm; //get hashtags
var string = 'mi alegría es total! ✌\n#fiestasdefindeaño #PadreHijo #buenosmomentos #france #paris';

var matches = (string.match(reg) || []).map(e => e.replace(reg, '$1'));
console.log(matches);

To zwróci: [fiestasdefindeaño, PadreHijo, buenosmomentos, france, paris]

 11
Author: Sebastien H.,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-05-25 07:28:29

Terminologia użyta w tej odpowiedzi:

  • dopasowanie wskazuje wynik uruchomienia wzorca RegEx na Twoim łańcuchu w następujący sposób: someString.match(regexPattern).
  • dopasowane wzorce wskazują wszystkie dopasowane części łańcucha wejściowego, które znajdują się wewnątrz tablicy match. Są to wszystkie instancje wzorca wewnątrz ciągu wejściowego.
  • dopasowane grupy wskazują wszystkie grupy do przechwycenia, zdefiniowane we wzorze RegEx. (Wzory wewnątrz nawiasy, jak TAK: /format_(.*?)/g, Gdzie (.*?) będzie dopasowaną grupą.) Te rezydują w dopasowanych wzorców .

Opis

Aby uzyskać dostęp do dopasowanych grup , w każdym z dopasowanych wzorców , potrzebujesz funkcji lub czegoś podobnego do iteracji nad dopasowaniem . Istnieje wiele sposobów, aby to zrobić, jak pokazuje wiele innych odpowiedzi. Większość innych odpowiedzi używa pętli while do iteracji wszystkich dopasowanych wzorce, ale myślę, że wszyscy znamy potencjalne zagrożenia związane z tym podejściem. Konieczne jest dopasowanie do new RegExp() zamiast tylko samego wzoru, o którym wspomniano tylko w komentarzu. Dzieje się tak dlatego, że metoda .exec() zachowuje się podobnie do funkcji generatora zatrzymuje się za każdym razem, gdy jest dopasowanie , ale zachowuje .lastIndex, aby kontynuować od tego momentu przy następnym wywołaniu .exec().

Przykłady kodu

Poniżej znajduje się przykład funkcji searchString, która zwraca Array ze wszystkich dopasowanych wzorców , gdzie każdy match jest Array ze wszystkimi zawierającymi dopasowanymi grupami . Zamiast używać pętli while, podałem przykłady używając zarówno funkcji Array.prototype.map(), jak i bardziej wydajnego sposobu-używając zwykłej pętli for.

Zwięzłe wersje (mniej kodu, więcej cukru składniowego)

Są one mniej wydajne, ponieważ zasadniczo implementują forEach-loop zamiast szybszej for-loop.

// Concise ES6/ES2015 syntax
const searchString = 
    (string, pattern) => 
        string
        .match(new RegExp(pattern.source, pattern.flags))
        .map(match => 
            new RegExp(pattern.source, pattern.flags)
            .exec(match));

// Or if you will, with ES5 syntax
function searchString(string, pattern) {
    return string
        .match(new RegExp(pattern.source, pattern.flags))
        .map(match =>
            new RegExp(pattern.source, pattern.flags)
            .exec(match));
}

let string = "something format_abc",
    pattern = /(?:^|\s)format_(.*?)(?:\s|$)/;

let result = searchString(string, pattern);
// [[" format_abc", "abc"], null]
// The trailing `null` disappears if you add the `global` flag

(więcej kodu, mniej cukru składniowego)

// Performant ES6/ES2015 syntax
const searchString = (string, pattern) => {
    let result = [];

    const matches = string.match(new RegExp(pattern.source, pattern.flags));

    for (let i = 0; i < matches.length; i++) {
        result.push(new RegExp(pattern.source, pattern.flags).exec(matches[i]));
    }

    return result;
};

// Same thing, but with ES5 syntax
function searchString(string, pattern) {
    var result = [];

    var matches = string.match(new RegExp(pattern.source, pattern.flags));

    for (var i = 0; i < matches.length; i++) {
        result.push(new RegExp(pattern.source, pattern.flags).exec(matches[i]));
    }

    return result;
}

let string = "something format_abc",
    pattern = /(?:^|\s)format_(.*?)(?:\s|$)/;

let result = searchString(string, pattern);
// [[" format_abc", "abc"], null]
// The trailing `null` disappears if you add the `global` flag

Muszę jeszcze porównać te alternatywy do tych wcześniej wymienionych w innych odpowiedziach, ale wątpię, że to podejście jest mniej wydajne i mniej bezpieczne niż inne.

 8
Author: Daniel Hallgren,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-08-23 22:36:09

Nie ma potrzeby wywoływania metody exec! Możesz użyć metody "match" bezpośrednio na łańcuchu. Tylko nie zapomnij o nawiasach.

var str = "This is cool";
var matches = str.match(/(This is)( cool)$/);
console.log( JSON.stringify(matches) ); // will print ["This is cool","This is"," cool"] or something like that...

Pozycja 0 ma ciąg znaków ze wszystkimi wynikami. Pozycja 1 ma pierwszy mecz reprezentowany w nawiasie, a pozycja 2 ma drugi mecz wyodrębniony w nawiasie. Zagnieżdżone nawiasy są trudne, więc uważaj!

 6
Author: Andre Carneiro,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-08-11 18:58:09

Jedna linijka, która jest praktyczna tylko wtedy, gdy masz jedną parę nawiasów:

while ( ( match = myRegex.exec( myStr ) ) && matches.push( match[1] ) ) {};
 5
Author: Nabil Kadimi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-07-12 15:41:47

Użycie kodu:

console.log(arr[1]);  // prints: abc
console.log(arr[0]);  // prints:  format_abc

Edit: Safari 3, jeśli ma to znaczenie.

 4
Author: eyelidlessness,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-01-11 07:27:02

function getMatches(string, regex, index) {
  index || (index = 1); // default to the first capturing group
  var matches = [];
  var match;
  while (match = regex.exec(string)) {
    matches.push(match[index]);
  }
  return matches;
}


// Example :
var myString = 'Rs.200 is Debited to A/c ...2031 on 02-12-14 20:05:49 (Clear Bal Rs.66248.77) AT ATM. TollFree 1800223344 18001024455 (6am-10pm)';
var myRegEx = /clear bal.+?(\d+\.?\d{2})/gi;

// Get an array containing the first capturing group for every match
var matches = getMatches(myString, myRegEx, 1);

// Log results
document.write(matches.length + ' matches found: ' + JSON.stringify(matches))
console.log(matches);

function getMatches(string, regex, index) {
  index || (index = 1); // default to the first capturing group
  var matches = [];
  var match;
  while (match = regex.exec(string)) {
    matches.push(match[index]);
  }
  return matches;
}


// Example :
var myString = 'something format_abc something format_def something format_ghi';
var myRegEx = /(?:^|\s)format_(.*?)(?:\s|$)/g;

// Get an array containing the first capturing group for every match
var matches = getMatches(myString, myRegEx, 1);

// Log results
document.write(matches.length + ' matches found: ' + JSON.stringify(matches))
console.log(matches);
 3
Author: Jack,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-06-22 20:38:46

Twój kod działa dla mnie (FF3 na Mac) nawet jeśli zgadzam się z PhiLo że regex powinien być prawdopodobnie:

/\bformat_(.*?)\b/

(ale, oczywiście, nie jestem pewien, ponieważ nie znam kontekstu wyrażeń regularnych.)

 1
Author: PEZ,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:34:50
/*Regex function for extracting object from "window.location.search" string.
 */

var search = "?a=3&b=4&c=7"; // Example search string

var getSearchObj = function (searchString) {

    var match, key, value, obj = {};
    var pattern = /(\w+)=(\w+)/g;
    var search = searchString.substr(1); // Remove '?'

    while (match = pattern.exec(search)) {
        obj[match[0].split('=')[0]] = match[0].split('=')[1];
    }

    return obj;

};

console.log(getSearchObj(search));
 0
Author: Pawel Kwiecien,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-06-27 18:47:53