Именованные группы захвата в регулярном выражении JavaScript? - PullRequest
178 голосов
/ 20 марта 2011

Насколько я знаю, в JavaScript нет такой вещи, как именованные группы захвата. Какой альтернативный способ получить подобную функциональность?

Ответы [ 9 ]

93 голосов
/ 20 марта 2011

ECMAScript 2018 вводит именованные группы захвата в регулярные выражения JavaScript.

Если вам требуется поддержка старых браузеров, вы можете делать все с обычными (нумерованными) группами захвата, которые вы можете делать с именованнымизахват групп, вам просто нужно следить за числами - что может быть громоздким, если меняется порядок захвата группы в вашем регулярном выражении.

Есть только два «структурных» преимущества именованных групп захвата, о которых я могу думать:

  1. В некоторых разновидностях регулярных выражений (.NET и JGSoft, насколько я знаю), вы можете использовать одно и то же имя для разных групп в вашем регулярном выражении ( см. здесь пример, где это имеет значение ).Но большинство разновидностей регулярных выражений в любом случае не поддерживают эту функцию.

  2. Если вам нужно обратиться к пронумерованным группам захвата в ситуации, когда они окружены цифрами, вы можете столкнуться с проблемой.Допустим, вы хотите добавить ноль к цифре и поэтому хотите заменить (\d) на $10.В JavaScript это будет работать (если в вашем регулярном выражении меньше 10 групп захвата), но Perl будет думать, что вы ищете номер обратной ссылки 10 вместо числа 1, за которым следует 0.В Perl вы можете использовать ${1}0 в этом случае.

Кроме того, именованные группы захвата являются просто "синтаксическим сахаром".Это помогает использовать группы захвата только тогда, когда они вам действительно нужны, и использовать группы без захвата (?:...) во всех других обстоятельствах.

Большая проблема (на мой взгляд) с JavaScript состоит в том, что он не поддерживает многословныйрегулярные выражения, которые значительно упростили бы создание читаемых сложных регулярных выражений.

Библиотека XRegExp Стива Левитана решает эти проблемы.

60 голосов
/ 16 ноября 2011

Вы можете использовать XRegExp , расширенную, расширяемую кросс-браузерную реализацию регулярных выражений, включая поддержку дополнительного синтаксиса, флагов и методов:

  • Добавляет новое регулярное выражениеи текстовый синтаксис замены, включая всестороннюю поддержку именованного захвата .
  • Добавляет два новых флага регулярных выражений: s, чтобы точка соответствовала всем символам (также известный как точка или однострочный режим), и x, для свободного пробела и комментариев (он же расширенный режим).
  • Предоставляет набор функций и методов, которые упрощают обработку сложных регулярных выражений.
  • Автоматически исправляет наиболее часто встречающиеся перекрестные выражения.несоответствия браузера в поведении и синтаксисе регулярных выражений.
  • Позволяет легко создавать и использовать плагины, которые добавляют новый синтаксис и флаги для языка регулярных выражений XRegExp.
56 голосов
/ 16 февраля 2016

Другое возможное решение: создать объект, содержащий имена и индексы групп.

var regex = new RegExp("(.*) (.*)");
var regexGroups = { FirstName: 1, LastName: 2 };

Затем используйте ключи объекта для ссылки на группы:

var m = regex.exec("John Smith");
var f = m[regexGroups.FirstName];

Это улучшает удобочитаемость/ качество кода с использованием результатов регулярного выражения, но не читаемость самого регулярного выражения.

51 голосов
/ 17 мая 2016

В 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)
16 голосов
/ 14 июня 2017

Обновление: наконец-то оно превращено в JavaScript (ECMAScript 2018)!


Именованные группы захвата могут очень скоро превратиться в JavaScript.
Предложениепоскольку это уже на этапе 3.

Группе захвата может быть присвоено имя в угловых скобках с использованием синтаксиса (?<name>...) для любого имени идентификатора.Регулярное выражение для даты может быть записано как /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/u.Каждое имя должно быть уникальным и следовать грамматике для ECMAScript IdentifierName .

Доступ к именованным группам можно получить из свойств свойства groups результата регулярного выражения.Нумерованные ссылки на группы также создаются, как и для неназванных групп.Например:

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';
6 голосов
/ 20 марта 2011

Именование захваченных групп дает одно: меньше путаницы со сложными регулярными выражениями.

Это действительно зависит от вашего варианта использования, но, возможно, симпатичная печать вашего регулярного выражения может помочь.

Или вы можете попытаться определить константы для ссылки на ваши захваченные группы.

Комментарии могут также помочь показать другим, кто читает ваш код, что вы сделали.

В остальном я должен согласиться с ответом Тимса.

5 голосов
/ 12 декабря 2014

Существует библиотека node.js с именем named-regexp , которую вы можете использовать в своих проектах node.js (в браузере путем упаковки библиотеки с помощью browserify или других сценариев упаковки).Однако библиотеку нельзя использовать с регулярными выражениями, которые содержат неназванные группы захвата.

Если вы учитываете открывающие скобки захвата в своем регулярном выражении, вы можете создать отображение между именованными группами захвата и нумерованными группами захватаВаше регулярное выражение и может смешивать и сочетать свободно.Вам просто нужно удалить имена групп перед использованием регулярных выражений.Я написал три функции, которые демонстрируют это.Смотрите эту суть: https://gist.github.com/gbirke/2cc2370135b665eee3ef

2 голосов
/ 28 августа 2015

Хотя вы не можете сделать это с помощью обычного JavaScript, возможно, вы можете использовать некоторую функцию Array.prototype, например Array.prototype.reduce, чтобы превратить индексированные совпадения в именованные, используя некоторые magic .

Очевидно, что следующему решению потребуется, чтобы совпадения происходили по порядку:

// @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));
0 голосов
/ 28 июня 2019

Как Тим Пицкер сказал, что ECMAScript 2018 вводит именованные группы захвата в регулярные выражения JavaScript. Но что я не нашел в ответах выше, так это как использовать именованную захваченную группу в самом регулярном выражении.

вы можете использовать именованную захваченную группу с этим синтаксисом: \k<name>. например

var regexObj = /(?<year>\d{4})-(?<day>\d{2})-(?<month>\d{2}) year is \k<year>/

и как Форивин сказал, что вы можете использовать захваченную группу в результате объекта следующим образом:

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>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...