1753 Stimmen

Wie greift man auf die übereinstimmenden Gruppen in einem regulären JavaScript-Ausdruck zu?

Ich möchte einen Teil einer Zeichenkette mit einer regulärer Ausdruck und greifen dann auf die eingeklammerte Teilzeichenkette zu:

    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 (!!!)

Was mache ich falsch?


Ich habe entdeckt, dass es nichts falsch mit dem regulären Ausdruck Code oben: die tatsächliche Zeichenfolge, die ich getestet wurde gegen diese war:

"date format_%A"

Die Meldung, dass "%A" undefiniert ist, scheint ein sehr seltsames Verhalten zu sein, aber es steht nicht in direktem Zusammenhang mit dieser Frage, daher habe ich eine neue eröffnet, Warum wird bei einer übereinstimmenden Teilzeichenkette in JavaScript "undefiniert" zurückgegeben? .


Das Problem war, dass console.log nimmt seine Parameter wie eine printf Anweisung, und da die Zeichenkette, die ich protokolliert habe ( "%A" ) einen speziellen Wert hatte, wurde versucht, den Wert des nächsten Parameters zu finden.

28voto

Wiktor Stribiżew Punkte 551798

String#matchAll (siehe die Entwurf der Stufe 3 / Vorschlag vom 7. Dezember 2018 ), vereinfacht den Zugriff auf alle Gruppen im Match-Objekt (beachten Sie, dass Gruppe 0 das gesamte Match ist, während die weiteren Gruppen den Erfassungsgruppen im Muster entsprechen):

Mit matchAll verfügbar sind, können Sie die while Schleife und exec con /g ... Stattdessen, durch die Verwendung von matchAll erhalten Sie einen Iterator zurück, den Sie mit dem bequemeren for...of , Array-Verbreitung , oder Array.from() Konstruktionen

Diese Methode führt zu einem ähnlichen Ergebnis wie Regex.Matches in C#, re.finditer in Python, preg_match_all in PHP.

Sehen Sie sich eine JS-Demo an (getestet in Google Chrome 73.0.3683.67 (offizieller Build), beta (64-bit)):

var myString = "key1:value1, key2-value2!!@key3=value3";
var matches = myString.matchAll(/(\w+)[:=-](\w+)/g);
console.log([...matches]); // All match with capturing group values

El console.log([...matches]) zeigt

enter image description here

Sie können auch Übereinstimmungswerte oder bestimmte Gruppenwerte erhalten, indem Sie

let matchData = "key1:value1, key2-value2!!@key3=value3".matchAll(/(\w+)[:=-](\w+)/g)
var matches = [...matchData]; // Note matchAll result is not re-iterable

console.log(Array.from(matches, m => m[0])); // All match (Group 0) values
// => [ "key1:value1", "key2-value2", "key3=value3" ]
console.log(Array.from(matches, m => m[1])); // All match (Group 1) values
// => [ "key1", "key2", "key3" ]

ANMERKUNG : Siehe die Browser-Kompatibilität Einzelheiten.

22voto

Daniel Hallgren Punkte 488

In dieser Antwort verwendete Terminologie:

  • Spiel gibt das Ergebnis an, das sich ergibt, wenn Sie Ihr RegEx-Muster mit Ihrer Zeichenkette vergleichen: someString.match(regexPattern) .
  • Abgestimmte Muster zeigen alle übereinstimmenden Teile der Eingabezeichenkette an, die sich alle innerhalb der Spiel Array. Dies sind alle Instanzen Ihres Musters in der Eingabezeichenkette.
  • Abgestimmte Gruppen geben alle zu fangenden Gruppen an, die im RegEx-Muster definiert sind. (Die Muster in Klammern, etwa so: /format_(.*?)/g , donde (.*?) wäre eine gematchte Gruppe). Diese befinden sich innerhalb übereinstimmende Muster .

Beschreibung

Um Zugang zum abgestimmte Gruppen in jeder der übereinstimmende Muster brauchen Sie eine Funktion oder etwas Ähnliches, um die Daten zu iterieren. Spiel . Es gibt eine Reihe von Möglichkeiten, dies zu tun, wie viele der anderen Antworten zeigen. Die meisten anderen Antworten verwenden eine while-Schleife, um über alle übereinstimmende Muster aber ich denke, wir alle kennen die potenziellen Gefahren, die mit diesem Ansatz verbunden sind. Es ist notwendig, gegen eine new RegExp() und nicht nur das Muster selbst, das nur in einem Kommentar erwähnt wurde. Dies liegt daran, dass die .exec() Methode verhält sich ähnlich wie eine Generatorfunktion - es wird jedes Mal angehalten, wenn es eine Übereinstimmung gibt sondern behält seine .lastIndex um von dort aus mit der nächsten .exec() anrufen.

Code-Beispiele

Nachfolgend ein Beispiel für eine Funktion searchString die eine Array von allen übereinstimmende Muster , wobei jede match ist ein Array mit allen enthaltenen abgestimmte Gruppen . Anstatt eine while-Schleife zu verwenden, habe ich Beispiele gegeben, die sowohl die Array.prototype.map() Funktion als auch eine leistungsfähigere Methode - die Verwendung einer einfachen for -Schleife.

Prägnante Versionen (weniger Code, mehr syntaktischer Zucker)

Diese sind weniger leistungsfähig, da sie im Grunde eine forEach -Schleife anstelle der schnelleren for -Schleife.

// 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

Leistungsfähige Versionen (mehr Code, weniger syntaktischer Zucker)

// 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

Ich muss diese Alternativen noch mit den in den anderen Antworten genannten vergleichen, aber ich bezweifle, dass dieser Ansatz weniger leistungsfähig und weniger ausfallsicher ist als die anderen.

18voto

Andre Carneiro Punkte 667

Es ist nicht notwendig, die exec Methode! Sie können die Methode "match" direkt auf die Zeichenkette anwenden. Vergessen Sie nur nicht die Klammern.

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...

Position 0 enthält eine Zeichenkette mit allen Ergebnissen. An Position 1 wird die erste Übereinstimmung durch Klammern dargestellt, und an Position 2 wird die zweite Übereinstimmung in Ihren Klammern isoliert. Verschachtelte Klammern sind knifflig, seien Sie also vorsichtig!

18voto

Jonathan Lonowski Punkte 116832

Ihre Syntax ist wahrscheinlich nicht die beste, um sie beizubehalten. FF/Gecko definiert RegExp als eine Erweiterung von Function.
(FF2 ging sogar so weit, dass typeof(/pattern/) == 'function' )

Es scheint, dass dies spezifisch für FF ist - IE, Opera und Chrome lösen alle Ausnahmen dafür aus.

Verwenden Sie stattdessen eine der beiden bereits von anderen genannten Methoden: RegExp#exec o String#match .
Sie bieten die gleichen Ergebnisse:

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"]

10voto

David Cheung Punkte 1458

Mit es2018 können Sie jetzt String.match() mit benannten Gruppen, macht Ihre Regex deutlicher, was sie zu tun versucht hat.

const url =
  'https://stackoverflow.com/questions/432493/how-do-you-access-the-matched-groups-in-a-javascript-regular-expression?some=parameter';
const regex = /(?<protocol>https?):\/\/(?<hostname>[\w-\.]*)\/(?<pathname>[\w-\./]+)\??(?<querystring>.*?)?$/;
const { groups: segments } = url.match(regex);
console.log(segments);

und Sie erhalten dann etwas wie

{Protokoll: "https", hostname: "stackoverflow.com", pathname: "questions/432493/how-do-you-access-the-matched-groups-in-a-javascript-regular-expression", querystring: "some=parameter"}

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X