Наскільки я знаю, немає такого поняття, як названі групи захоплення в JavaScript. Який альтернативний спосіб отримати подібний функціонал?
Наскільки я знаю, немає такого поняття, як названі групи захоплення в JavaScript. Який альтернативний спосіб отримати подібний функціонал?
Відповіді:
ECMAScript 2018 вводить названі групи захоплення в регулярні вирази JavaScript.
Приклад:
const auth = 'Bearer AUTHORIZATION_TOKEN'
const { groups: { token } } = /Bearer (?<token>[^ $]*)/.exec(auth)
console.log(token) // "Prints AUTHORIZATION_TOKEN"
Якщо вам потрібно підтримувати старі веб-переглядачі, ви можете робити все із звичайними (пронумерованими) групами захоплення, які ви можете зробити з названими групами захоплення, вам просто потрібно слідкувати за номерами - що може бути громіздким, якщо порядок захоплення групи у вашій зміни регулярного вираження.
Є лише дві "структурні" переваги названих груп захоплення, про які я можу придумати:
У деяких ароматах регулярного вираження (.NET і JGSoft, наскільки я знаю), ви можете використовувати те саме ім'я для різних груп у вашому регулярному виразі ( див. Тут приклад, де це має значення ). Але більшість ароматів регулярного вираження так чи інакше не підтримують цю функціональність.
Якщо вам потрібно посилатися на нумеровані групи захоплення в ситуації, коли вони оточені цифрами, ви можете отримати проблему. Скажімо, ви хочете додати нуль до цифри і тому хочете замінити (\d)
на $10
. У JavaScript це буде працювати (доки у вас в регексе буде менше 10 груп захоплення), але Perl подумає, що ви шукаєте номер зворотної референції 10
замість номера 1
, а потім - a 0
. У Perl ви можете використовувати ${1}0
в цьому випадку.
Крім того, названі групи захоплення - це лише «синтаксичний цукор». Це допомагає використовувати групи захоплення лише тоді, коли вони вам справді потрібні, а також використовувати групи, які не захоплюють, (?:...)
за будь-яких інших обставин.
Більшою проблемою (на мою думку) у JavaScript є те, що він не підтримує багатослівних регулярних виразів, що значно спростить створення читаних, складних регулярних виразів.
Бібліотека XRegExp Стіва Левітана вирішує ці проблеми.
Ви можете використовувати XRegExp , розширену, розширювану, крос-браузерну реалізацію регулярних виразів, включаючи підтримку додаткових синтаксисів, прапорів та методів:
s
виразів:, щоб зрівняти крапку з усіма символами (також режимом доталі або однолінійного режиму), а також x
для вільного проміжку та коментарів (також розширений режим).Ще одне можливе рішення: створити об’єкт, що містить назви груп та індекси.
var regex = new RegExp("(.*) (.*)");
var regexGroups = { FirstName: 1, LastName: 2 };
Потім використовуйте клавіші об'єкта для посилання на групи:
var m = regex.exec("John Smith");
var f = m[regexGroups.FirstName];
Це покращує читабельність / якість коду за допомогою результатів регулярного вираження, але не читабельність самого регулярного виразу.
У ES6 ви можете використовувати деструктування масиву, щоб спіймати ваші групи:
let text = '27 months';
let regex = /(\d+)\s*(days?|months?|years?)/;
let [, count, unit] = regex.exec(text) || [];
// count === '27'
// unit === 'months'
Зверніть увагу:
let
пропускає перше значення результуючого масиву, яке є всім узгодженим рядком|| []
після .exec()
запобіжить помилки деструктурірующіе коли немає матчів (бо .exec()
повернеться null
)String.prototype.match
повертає масив з: всього узгодженого рядка в позиції 0, а потім будь-яких груп після цього. Перша кома говорить "пропустіть елемент у позиції 0"
RegExp.prototype.exec
більше String.prototype.match
місця, де може бути рядок null
або undefined
.
Оновлення: нарешті перетворили його на JavaScript (ECMAScript 2018)!
Названі групи захоплення можуть перетворити його на JavaScript дуже скоро.
Пропозиція щодо нього вже є на 3 етапі.
Групі захоплення можна присвоїти ім’я у кутових дужках, використовуючи (?<name>...)
синтаксис, для будь-якого ідентифікатора. Регулярний вираз для дати тоді може бути записаний як/(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/u
. Кожне ім'я повинно бути унікальним і дотримуватися граматики ECMAScript IdentifierName .
До іменованих груп можна отримати доступ із властивостей властивості груп результату регулярного виразу. Також створюються нумеровані посилання на групи, як і для неіменованих груп. Наприклад:
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'));
Іменування захоплених груп забезпечує одне: менше плутанини зі складними регулярними виразами.
Це дійсно залежить від вашої справи використання, але, можливо, гарне друк вашого регексу може допомогти.
Або ви можете спробувати визначити константи для позначення захоплених груп.
Коментарі можуть також допомогти показати іншим, хто читає ваш код, що ви зробили.
В іншому я повинен погодитися з відповіддю Тімса.
Існує бібліотека node.js під назвою найменування-regexp яку ви могли б використовувати у своїх проектах node.js (у браузері, упаковуючи бібліотеку за допомогою браузера або інших сценаріїв упаковки). Однак бібліотеку не можна використовувати з регулярними виразами, які містять неіменовані групи захоплення.
Якщо ви підраховуєте вступні фіксуючі дужки в регулярному виразі, ви можете створити відображення між названими групами захоплення та пронумерованими групами захоплення у вашому регулярному виразі, і ви можете змішуватись і співставляти вільно. Потрібно просто видалити назви груп, перш ніж використовувати регулярний вираз. Я написав три функції, які це демонструють. Дивіться цю суть: https://gist.github.com/gbirke/2cc2370135b665eee3ef
Як зазначив Тім Пітцкер, ECMAScript 2018 вводить названі групи захоплення в регулярні вирази JavaScript. Але те, що я не знайшов у наведених вище відповідях, це те, як використовувати названу захоплену групу в самому регексе.
Ви можете використовувати захоплене з ім'ям групи з цим синтаксисом: \k<name>
. наприклад
var regexObj = /(?<year>\d{4})-(?<day>\d{2})-(?<month>\d{2}) year is \k<year>/
і як сказав Forivin, ви можете використовувати захоплену групу в результаті об'єкта, як слід:
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>
Хоча ви не можете зробити це за допомогою ванільного JavaScript, можливо, ви можете використовувати якусь Array.prototype
функцію, як Array.prototype.reduce
перетворити індексовані відповідники в названі, використовуючи якусь магію .
Очевидно, що наступне рішення знадобиться, щоб збіги відбувалися в порядку:
// @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
об'єкт, додавши функцію до його прототипу.
Не маєте ECMAScript 2018?
Моя мета полягала в тому, щоб він працював максимально схожим на те, до чого ми звикли з названими групами. Якщо в ECMAScript 2018 ви можете розмістити ?<groupname>
всередині групи, щоб вказати іменовану групу, у моєму рішенні для старих javascript ви можете розмістити (?!=<groupname>)
всередині групи, щоб зробити те саме. Отже, це додатковий набір дужок і додатковий !=
. Досить близько!
Я все це перетворив на функцію прототипу рядка
Особливості
Інструкції
(?!={groupname})
всередині кожної групи, яку ви хочете назвати()
, поставивши їх ?:
на початку. Вони не будуть названі.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;
}, {});
};
використання
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
}
результат о
{
"house number": "123",
"street name": "Main",
"street type": "St"
}