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.

2038voto

Christian C. Salvadó Punkte 763569

Sie können auf diese Weise auf die Erfassungsgruppen zugreifen:

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

Und wenn es mehrere Übereinstimmungen gibt, können Sie diese iterativ durchgehen:

var myString = "something format_abc";
var myRegexp = new RegExp("(?:^|\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);
}

Bearbeiten: 2019-09-10

Wie Sie sehen können, war die Art und Weise der Iteration über mehrere Übereinstimmungen nicht sehr intuitiv. Dies führte zu dem Vorschlag, die String.prototype.matchAll Methode. Diese neue Methode wird voraussichtlich in den nächsten ECMAScript 2020-Spezifikation . Es gibt uns eine saubere API und löst mehrere Probleme. Es wurde begonnen, auf den wichtigsten Browsern und JS-Engines zu landen als Chrome 73+ / Node 12+ und Firefox 67+.

Die Methode gibt einen Iterator zurück und wird wie folgt verwendet:

const string = "something format_abc";
const regexp = /(?:^|\s)format_(.*?)(?:\s|$)/g;
const matches = string.matchAll(regexp);

for (const match of matches) {
  console.log(match);
  console.log(match.index)
}

Da sie einen Iterator zurückgibt, kann man sagen, dass sie träge ist. Dies ist nützlich, wenn man eine besonders große Anzahl von Erfassungsgruppen oder sehr große Zeichenketten handhabt. Bei Bedarf kann das Ergebnis aber auch einfach in ein Array umgewandelt werden, indem man die Verbreitungssyntax oder die Array.from Methode:

function getFirstGroup(regexp, str) {
  const array = [...str.matchAll(regexp)];
  return array.map(m => m[1]);
}

// or:
function getFirstGroup(regexp, str) {
  return Array.from(str.matchAll(regexp), m => m[1]);
}

In der Zwischenzeit, bis dieser Vorschlag eine breitere Unterstützung findet, können Sie die offizielles Unterlegscheibenpaket .

Auch die interne Funktionsweise der Methode ist einfach. Eine äquivalente Implementierung mit einer Generatorfunktion würde wie folgt aussehen:

function* matchAll(str, regexp) {
  const flags = regexp.global ? regexp.flags : regexp.flags + "g";
  const re = new RegExp(regexp, flags);
  let match;
  while (match = re.exec(str)) {
    yield match;
  }
}

Es wird eine Kopie der ursprünglichen Regexp erstellt, um Nebenwirkungen durch die Mutation der Regexp zu vermeiden. lastIndex Eigenschaft beim Durchlaufen der Mehrfachtreffer.

Außerdem müssen wir sicherstellen, dass die Regexp die weltweit Flag, um eine Endlosschleife zu vermeiden.

Ich freue mich auch, dass sogar auf diese StackOverflow-Frage in der Diskussionen über den Vorschlag .

209voto

Mathias Bynens Punkte 136619

Hier ist eine Methode, die Sie verwenden können, um die n Erfassungsgruppe für jedes Spiel:

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

71voto

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

El \b ist nicht genau das Gleiche. (Es funktioniert bei --format_foo/ , funktioniert aber nicht bei format_a_b ) Aber ich wollte eine Alternative zu Ihrem Ausdruck aufzeigen, der in Ordnung ist. Natürlich ist die match Der Anruf ist das Wichtigste.

46voto

Sebastien H. Punkte 6010

Zu guter Letzt habe ich eine Codezeile gefunden, die bei mir gut funktioniert hat (JS ES6):

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

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

Dies wird zurückgegeben:

['fiestasdefindeaño', 'PadreHijo', 'buenosmomentos', 'france', 'paris']

37voto

Alexz Punkte 351

In Bezug auf die Multi-Match-Klammern Beispiele oben, war ich auf der Suche nach einer Antwort hier nach nicht bekommen, was ich wollte aus:

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

Nachdem ich mir die etwas verworrenen Funktionsaufrufe mit while und .push() oben angesehen hatte, dämmerte mir, dass das Problem stattdessen sehr elegant mit mystring.replace() gelöst werden kann (das Ersetzen ist NICHT der Punkt und wird auch nicht gemacht, sondern die saubere, eingebaute rekursive Funktionsaufrufoption für den zweiten Parameter!)

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

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

Danach werde ich wohl nie wieder .match() für so gut wie nichts mehr verwenden.

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