Per quanto ne so non esiste nulla come gruppi di acquisizione con nome in JavaScript. Qual è il modo alternativo per ottenere funzionalità simili?
Per quanto ne so non esiste nulla come gruppi di acquisizione con nome in JavaScript. Qual è il modo alternativo per ottenere funzionalità simili?
Risposte:
ECMAScript 2018 introduce gruppi di acquisizione denominati nelle regex JavaScript.
Esempio:
const auth = 'Bearer AUTHORIZATION_TOKEN'
const { groups: { token } } = /Bearer (?<token>[^ $]*)/.exec(auth)
console.log(token) // "Prints AUTHORIZATION_TOKEN"
Se hai bisogno di supportare browser più vecchi, puoi fare tutto con i normali gruppi di acquisizione (numerati) che puoi fare con i gruppi di acquisizione nominati, devi solo tenere traccia dei numeri, il che può essere ingombrante se l'ordine di acquisizione del gruppo nel tuo modifiche regex.
Ci sono solo due vantaggi "strutturali" dei gruppi di acquisizione nominati a cui posso pensare:
In alcune versioni di regex (.NET e JGSoft, per quanto ne so), puoi usare lo stesso nome per diversi gruppi nella tua regex ( vedi qui un esempio in cui è importante ). Ma la maggior parte dei sapori regex non supporta questa funzionalità comunque.
Se è necessario fare riferimento a gruppi di acquisizione numerati in una situazione in cui sono circondati da cifre, è possibile che si verifichi un problema. Supponiamo che tu voglia aggiungere uno zero a una cifra e quindi desideri sostituirlo (\d)con $10. In JavaScript, questo funzionerà (purché tu abbia meno di 10 gruppi di acquisizione nella tua regex), ma Perl penserà che stai cercando un numero di backreference 10invece di numero 1, seguito da a 0. In Perl, puoi usare ${1}0in questo caso.
Oltre a ciò, i gruppi di cattura nominati sono solo "zucchero sintattico". Aiuta a utilizzare i gruppi di acquisizione solo quando ne hai veramente bisogno e a utilizzare i gruppi di acquisizione(?:...) in tutte le altre circostanze.
Il problema più grande (secondo me) con JavaScript è che non supporta regex verbose che renderebbero molto più facile la creazione di espressioni regolari complesse leggibili.
La libreria XRegExp di Steve Levithan risolve questi problemi.
Puoi usare XRegExp , un'implementazione aumentata, estensibile e cross-browser di espressioni regolari, incluso il supporto per sintassi, flag e metodi aggiuntivi:
s:, per far corrispondere il punto a tutti i caratteri (aka dotall o singleline mode) e x, per spaziatura libera e commenti (aka modalità estesa).Un'altra possibile soluzione: creare un oggetto contenente i nomi e gli indici dei gruppi.
var regex = new RegExp("(.*) (.*)");
var regexGroups = { FirstName: 1, LastName: 2 };
Quindi, utilizzare le chiavi dell'oggetto per fare riferimento ai gruppi:
var m = regex.exec("John Smith");
var f = m[regexGroups.FirstName];
Ciò migliora la leggibilità / qualità del codice usando i risultati della regex, ma non la leggibilità della regex stessa.
In ES6 puoi usare l'array destructuring per catturare i tuoi gruppi:
let text = '27 months';
let regex = /(\d+)\s*(days?|months?|years?)/;
let [, count, unit] = regex.exec(text) || [];
// count === '27'
// unit === 'months'
Avviso:
letsalta il primo valore dell'array risultante, che è l'intera stringa corrispondente|| []dopo .exec()eviterà un errore di destrutturazione quando non ci sono corrispondenze (perché .exec()restituirà null)String.prototype.matchrestituisce un array con: l'intera stringa corrispondente nella posizione 0, quindi qualsiasi gruppo successivo. La prima virgola dice "salta l'elemento nella posizione 0"
RegExp.prototype.execsostituzione String.prototype.matchin luoghi in cui la stringa potrebbe essere nullo undefined.
Aggiornamento: finalmente è diventato JavaScript (ECMAScript 2018)!
I gruppi di acquisizione denominati potrebbero trasformarlo in JavaScript molto presto.
La proposta è già nella fase 3.
A un gruppo di acquisizione può essere assegnato un nome tra parentesi angolari utilizzando la (?<name>...)sintassi, per qualsiasi nome identificativo. L'espressione regolare per una data può quindi essere scritta come /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/u. Ogni nome deve essere univoco e seguire la grammatica per ECMAScript IdentifierName .
È possibile accedere ai gruppi con nome dalle proprietà di una proprietà di gruppi del risultato dell'espressione regolare. Vengono anche creati riferimenti numerati ai gruppi, proprio come per i gruppi senza nome. Per esempio:
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'));
La denominazione dei gruppi acquisiti fornisce una cosa: meno confusione con espressioni regolari complesse.
Dipende molto dal tuo caso d'uso, ma forse una bella stampa del tuo regex potrebbe aiutare.
Oppure potresti provare a definire le costanti da fare riferimento ai tuoi gruppi acquisiti.
I commenti potrebbero anche aiutare a mostrare agli altri che hanno letto il tuo codice, cosa hai fatto.
Per il resto devo essere d'accordo con la risposta di Tims.
Esiste una libreria node.js chiamata named-regexp che potresti usare nei tuoi progetti node.js (attiva nel browser comprimendo la libreria con browserify o altri script di packaging). Tuttavia, la libreria non può essere utilizzata con espressioni regolari che contengono gruppi di acquisizione senza nome.
Se conti le parentesi graffe di apertura nella tua espressione regolare puoi creare una mappatura tra i gruppi di cattura nominati e i gruppi di cattura numerati nella tua regex e puoi mescolarli e abbinarli liberamente. Devi solo rimuovere i nomi dei gruppi prima di usare regex. Ho scritto tre funzioni che lo dimostrano. Guarda questo riassunto: https://gist.github.com/gbirke/2cc2370135b665eee3ef
Come ha affermato Tim Pietzcker, ECMAScript 2018 introduce regex JavaScript nei gruppi di acquisizione denominati. Ma ciò che non ho trovato nelle risposte sopra è stato come utilizzare il gruppo acquisito con nome nella regex stessa.
è possibile utilizzare il nome del gruppo catturate con la seguente sintassi: \k<name>. per esempio
var regexObj = /(?<year>\d{4})-(?<day>\d{2})-(?<month>\d{2}) year is \k<year>/
e come ha detto Forivin puoi usare il gruppo acquisito nel risultato dell'oggetto come segue:
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>
Anche se non puoi farlo con JavaScript vaniglia, forse puoi usare alcune Array.prototypefunzioni come Array.prototype.reducetrasformare le partite indicizzate in nominali usando un po 'di magia .
Ovviamente, la seguente soluzione richiederà che si verifichino delle corrispondenze nell'ordine:
// @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}.+)");
RegExpoggetto aggiungendo una funzione al suo prototipo.
Non hai ECMAScript 2018?
Il mio obiettivo era di farlo funzionare il più simile possibile a quello a cui siamo abituati con i gruppi denominati. Mentre in ECMAScript 2018 puoi inserire ?<groupname>all'interno del gruppo per indicare un gruppo nominato, nella mia soluzione per javascript precedente, puoi inserire (?!=<groupname>)all'interno del gruppo per fare la stessa cosa. Quindi è un set extra di parentesi e un extra !=. Molto vicino!
Ho avvolto tutto in una funzione prototipo di stringa
Caratteristiche
Istruzioni
(?!={groupname})all'interno di ogni gruppo a cui vuoi dare un nome()mettendo ?:all'inizio di quel gruppo. Questi non saranno nominati.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;
}, {});
};
uso
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
}
risultato di o
{
"house number": "123",
"street name": "Main",
"street type": "St"
}