Soweit ich weiß, gibt es in JavaScript keine benannten Erfassungsgruppen. Was ist der alternative Weg, um ähnliche Funktionen zu erhalten?
Soweit ich weiß, gibt es in JavaScript keine benannten Erfassungsgruppen. Was ist der alternative Weg, um ähnliche Funktionen zu erhalten?
Antworten:
ECMAScript 2018 führt benannte Erfassungsgruppen in JavaScript-Regexes ein.
Beispiel:
const auth = 'Bearer AUTHORIZATION_TOKEN'
const { groups: { token } } = /Bearer (?<token>[^ $]*)/.exec(auth)
console.log(token) // "Prints AUTHORIZATION_TOKEN"
Wenn Sie ältere Browser unterstützen müssen, können Sie alles mit normalen (nummerierten) Erfassungsgruppen tun, was Sie mit benannten Erfassungsgruppen tun können. Sie müssen nur die Zahlen im Auge behalten - was möglicherweise umständlich ist, wenn die Reihenfolge der Erfassungsgruppe in Ihrer Regex-Änderungen.
Ich kann mir nur zwei "strukturelle" Vorteile benannter Erfassungsgruppen vorstellen:
In einigen Regex-Varianten (.NET und JGSoft, soweit ich weiß) können Sie denselben Namen für verschiedene Gruppen in Ihrer Regex verwenden ( siehe hier für ein Beispiel, wo dies wichtig ist ). Die meisten Regex-Varianten unterstützen diese Funktionalität jedoch ohnehin nicht.
Wenn Sie sich in einer Situation, in der sie von Ziffern umgeben sind, auf nummerierte Erfassungsgruppen beziehen müssen, kann ein Problem auftreten. Angenommen , Sie haben eine Null zu einer Ziffer hinzugefügt werden soll und daher ersetzt werden soll (\d)
mit $10
. In JavaScript funktioniert dies (solange Sie weniger als 10 Erfassungsgruppen in Ihrem regulären Ausdruck haben), aber Perl wird denken, dass Sie nach einer Referenznummer 10
anstelle einer Nummer suchen 1
, gefolgt von einer 0
. In Perl können Sie ${1}0
in diesem Fall verwenden.
Ansonsten sind benannte Erfassungsgruppen nur "syntaktischer Zucker". Es ist hilfreich, Erfassungsgruppen nur dann zu verwenden, wenn Sie sie wirklich benötigen, und unter (?:...)
allen anderen Umständen nicht erfasste Gruppen zu verwenden .
Das größere Problem (meiner Meinung nach) mit JavaScript ist, dass es keine ausführlichen regulären Ausdrücke unterstützt, was die Erstellung lesbarer, komplexer regulärer Ausdrücke erheblich erleichtern würde.
Die XRegExp-Bibliothek von Steve Levithan löst diese Probleme.
Sie können XRegExp verwenden , eine erweiterte, erweiterbare, browserübergreifende Implementierung regulärer Ausdrücke, einschließlich der Unterstützung zusätzlicher Syntax, Flags und Methoden:
s
, damit der Punkt mit allen Zeichen übereinstimmt (auch bekannt als Dotall- oder Singleline-Modus) und x
für freie Abstände und Kommentare (auch als erweiterter Modus bezeichnet).Eine andere mögliche Lösung: Erstellen Sie ein Objekt, das die Gruppennamen und Indizes enthält.
var regex = new RegExp("(.*) (.*)");
var regexGroups = { FirstName: 1, LastName: 2 };
Verwenden Sie dann die Objektschlüssel, um auf die Gruppen zu verweisen:
var m = regex.exec("John Smith");
var f = m[regexGroups.FirstName];
Dies verbessert die Lesbarkeit / Qualität des Codes unter Verwendung der Ergebnisse des regulären Ausdrucks, jedoch nicht der Lesbarkeit des regulären Ausdrucks selbst.
In ES6 können Sie die Array-Destrukturierung verwenden, um Ihre Gruppen abzufangen:
let text = '27 months';
let regex = /(\d+)\s*(days?|months?|years?)/;
let [, count, unit] = regex.exec(text) || [];
// count === '27'
// unit === 'months'
Beachten:
let
überspringt den ersten Wert des resultierenden Arrays, bei dem es sich um die gesamte übereinstimmende Zeichenfolge handelt|| []
nach .exec()
einem Destrukturierung Fehler vermeiden , wenn es keine Übereinstimmungen geben (weil .exec()
zurückkehren null
)String.prototype.match
Gibt ein Array zurück mit: der gesamten übereinstimmenden Zeichenfolge an Position 0, danach alle Gruppen danach. Das erste Komma sagt "überspringe das Element an Position 0"
RegExp.prototype.exec
über String.prototype.match
an Orten , wo die Zeichenfolge sein kann , null
oder undefined
.
Update: Es hat es endlich in JavaScript geschafft (ECMAScript 2018)!
Benannte Erfassungsgruppen könnten es sehr bald in JavaScript schaffen.
Der Vorschlag dafür befindet sich bereits in Phase 3.
Einer Erfassungsgruppe kann mithilfe der (?<name>...)
Syntax für jeden Bezeichnernamen ein Name in eckigen Klammern zugewiesen werden. Der reguläre Ausdruck für ein Datum kann dann wie folgt geschrieben werden /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/u
. Jeder Name sollte eindeutig sein und der Grammatik für ECMAScript IdentifierName folgen .
Auf benannte Gruppen kann über die Eigenschaften einer Gruppeneigenschaft des Ergebnisses des regulären Ausdrucks zugegriffen werden. Ebenso wie für nicht benannte Gruppen werden nummerierte Verweise auf die Gruppen erstellt. Beispielsweise:
let re = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/u;
let result = re.exec('2015-01-02');
// result.groups.year === '2015';
// result.groups.month === '01';
// result.groups.day === '02';
// result[0] === '2015-01-02';
// result[1] === '2015';
// result[2] === '01';
// result[3] === '02';
let {year, month, day} = ((result) => ((result) ? result.groups : {}))(re.exec('2015-01-02'));
Die Benennung erfasster Gruppen bietet eines: weniger Verwirrung mit komplexen regulären Ausdrücken.
Es hängt wirklich von Ihrem Anwendungsfall ab, aber vielleicht könnte es hilfreich sein, Ihren regulären Ausdruck hübsch auszudrucken.
Oder Sie können versuchen, Konstanten zu definieren, die auf Ihre erfassten Gruppen verweisen.
Kommentare können dann auch dazu beitragen, anderen, die Ihren Code lesen, zu zeigen, was Sie getan haben.
Im Übrigen muss ich Tims Antwort zustimmen.
Es gibt eine node.js-Bibliothek namens named-regexp , die Sie in Ihren node.js-Projekten verwenden können (im Browser durch Packen der Bibliothek mit browserify oder anderen Paketskripten). Die Bibliothek kann jedoch nicht mit regulären Ausdrücken verwendet werden, die nicht benannte Erfassungsgruppen enthalten.
Wenn Sie die öffnenden Erfassungsklammern in Ihrem regulären Ausdruck zählen, können Sie eine Zuordnung zwischen benannten Erfassungsgruppen und den nummerierten Erfassungsgruppen in Ihrer Regex erstellen und diese frei mischen und anpassen. Sie müssen nur die Gruppennamen entfernen, bevor Sie den regulären Ausdruck verwenden können. Ich habe drei Funktionen geschrieben, die das demonstrieren. Siehe das Wesentliche: https://gist.github.com/gbirke/2cc2370135b665eee3ef
Wie Tim Pietzcker sagte, führt ECMAScript 2018 benannte Erfassungsgruppen in JavaScript-Regexes ein. Was ich in den obigen Antworten jedoch nicht gefunden habe, war, wie die benannte erfasste Gruppe im regulären Ausdruck selbst verwendet wird.
Sie können benannte erfasste Gruppen mit folgender Syntax verwenden : \k<name>
. beispielsweise
var regexObj = /(?<year>\d{4})-(?<day>\d{2})-(?<month>\d{2}) year is \k<year>/
und wie Forivin sagte, können Sie die erfasste Gruppe im Objektergebnis wie folgt verwenden:
let result = regexObj.exec('2019-28-06 year is 2019');
// result.groups.year === '2019';
// result.groups.month === '06';
// result.groups.day === '28';
var regexObj = /(?<year>\d{4})-(?<day>\d{2})-(?<month>\d{2}) year is \k<year>/mgi;
function check(){
var inp = document.getElementById("tinput").value;
let result = regexObj.exec(inp);
document.getElementById("year").innerHTML = result.groups.year;
document.getElementById("month").innerHTML = result.groups.month;
document.getElementById("day").innerHTML = result.groups.day;
}
td, th{
border: solid 2px #ccc;
}
<input id="tinput" type="text" value="2019-28-06 year is 2019"/>
<br/>
<br/>
<span>Pattern: "(?<year>\d{4})-(?<day>\d{2})-(?<month>\d{2}) year is \k<year>";
<br/>
<br/>
<button onclick="check()">Check!</button>
<br/>
<br/>
<table>
<thead>
<tr>
<th>
<span>Year</span>
</th>
<th>
<span>Month</span>
</th>
<th>
<span>Day</span>
</th>
</tr>
</thead>
<tbody>
<tr>
<td>
<span id="year"></span>
</td>
<td>
<span id="month"></span>
</td>
<td>
<span id="day"></span>
</td>
</tr>
</tbody>
</table>
Während Sie dies mit Vanille-JavaScript nicht tun können, können Sie möglicherweise eine Array.prototype
Funktion verwenden Array.prototype.reduce
, um indizierte Übereinstimmungen mit etwas Magie in benannte Übereinstimmungen umzuwandeln .
Für die folgende Lösung müssen die Übereinstimmungen natürlich in der richtigen Reihenfolge erfolgen:
// @text Contains the text to match
// @regex A regular expression object (f.e. /.+/)
// @matchNames An array of literal strings where each item
// is the name of each group
function namedRegexMatch(text, regex, matchNames) {
var matches = regex.exec(text);
return matches.reduce(function(result, match, index) {
if (index > 0)
// This substraction is required because we count
// match indexes from 1, because 0 is the entire matched string
result[matchNames[index - 1]] = match;
return result;
}, {});
}
var myString = "Hello Alex, I am John";
var namedMatches = namedRegexMatch(
myString,
/Hello ([a-z]+), I am ([a-z]+)/i,
["firstPersonName", "secondPersonName"]
);
alert(JSON.stringify(namedMatches));
var assocArray = Regex("hello alex, I am dennis", "hello ({hisName}.+), I am ({yourName}.+)");
RegExp
Objekt erweitern, indem Sie seinem Prototyp eine Funktion hinzufügen.
Sie haben kein ECMAScript 2018?
Mein Ziel war es, es so ähnlich wie möglich zu machen, wie wir es von benannten Gruppen gewohnt sind. Während Sie in ECMAScript 2018 ?<groupname>
innerhalb der Gruppe eine benannte Gruppe angeben können, können Sie in meiner Lösung für älteres Javascript (?!=<groupname>)
innerhalb der Gruppe platzieren, um dasselbe zu tun. Es ist also ein zusätzlicher Satz Klammern und ein zusätzlicher !=
. Ziemlich knapp!
Ich habe alles in eine String-Prototyp-Funktion eingewickelt
Eigenschaften
Anleitung
(?!={groupname})
innerhalb jeder Gruppe, die Sie benennen möchten()
indem Sie sie ?:
an den Anfang dieser Gruppe setzen. Diese werden nicht benannt.arrays.js
// @@pattern - includes injections of (?!={groupname}) for each group
// @@returns - an object with a property for each group having the group's match as the value
String.prototype.matchWithGroups = function (pattern) {
var matches = this.match(pattern);
return pattern
// get the pattern as a string
.toString()
// suss out the groups
.match(/<(.+?)>/g)
// remove the braces
.map(function(group) {
return group.match(/<(.+)>/)[1];
})
// create an object with a property for each group having the group's match as the value
.reduce(function(acc, curr, index, arr) {
acc[curr] = matches[index + 1];
return acc;
}, {});
};
Verwendung
function testRegGroups() {
var s = '123 Main St';
var pattern = /((?!=<house number>)\d+)\s((?!=<street name>)\w+)\s((?!=<street type>)\w+)/;
var o = s.matchWithGroups(pattern); // {'house number':"123", 'street name':"Main", 'street type':"St"}
var j = JSON.stringify(o);
var housenum = o['house number']; // 123
}
Ergebnis von o
{
"house number": "123",
"street name": "Main",
"street type": "St"
}