Как вы получаете доступ к соответствующим группам в регулярном выражении JavaScript? - PullRequest
1192 голосов
/ 11 января 2009

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

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 (!!!)

Что я делаю не так?


Я обнаружил, что в приведенном выше коде регулярного выражения нет ничего неправильного: фактическая строка, с которой я проверял, была такой:

"date format_%A"

Сообщение о том, что «% A» не определено, кажется очень странным поведением, но оно не имеет прямого отношения к этому вопросу, поэтому я открыл новый, Почему совпадающая подстрока возвращает «undefined» "в JavaScript? .


Проблема заключалась в том, что console.log принимает свои параметры как оператор printf, и поскольку строка, которую я записывал ("%A"), имела специальное значение, она пыталась найти значение следующего параметра.

Ответы [ 16 ]

1501 голосов
/ 11 января 2009

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

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

И если есть несколько совпадений, вы можете перебрать их:

var myString = "something format_abc";
var myRegexp = /(?:^|\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);
}
170 голосов
/ 08 января 2013

Вот метод, который вы можете использовать, чтобы получить n группу захвата для каждого матча:

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);
53 голосов
/ 11 января 2009

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

\b не совсем то же самое. (Он работает на --format_foo/, но не работает на format_a_b) Но я хотел показать альтернативу вашему выражению, что вполне нормально. Конечно, звонок match важен.

28 голосов
/ 17 июля 2014

Что касается приведенных выше примеров с круглыми скобками, я искал здесь ответ, не получив того, что хотел:

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

Посмотрев на слегка запутанные вызовы функций с помощью while и .push () выше, я понял, что проблему можно очень элегантно решить с помощью mystring.replace () вместо этого (замена НЕ является точкой, Даже если сделать, CLEAN, опция вызова встроенной рекурсивной функции для второго параметра:):

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

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

После этого, я не думаю, что когда-либо снова буду использовать .match () для чего-либо еще.

20 голосов
/ 03 января 2017

И последнее, но не менее важное: я нашел одну строку кода, которая отлично сработала для меня (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);

Это вернет:

['fiestasdefindeaño', 'PadreHijo', 'buenosmomentos', 'france', 'paris']
16 голосов
/ 11 января 2009

Ваш синтаксис, вероятно, не самый лучший для сохранения. FF / Gecko определяет RegExp как расширение функции.
(FF2 дошел до typeof(/pattern/) == 'function')

Похоже, что это специфично для FF - IE, Opera и Chrome все бросают исключения для него.

Вместо этого используйте любой из методов, упомянутых ранее: RegExp#exec или String#match.
Они предлагают одинаковые результаты:

var regex = /(?:^|\s)format_(.*?)(?:\s|$)/;
var input = "something format_abc";

regex(input);        //=> [" format_abc", "abc"]
regex.exec(input);   //=> [" format_abc", "abc"]
input.match(regex);  //=> [" format_abc", "abc"]
13 голосов
/ 24 августа 2017

Терминология, использованная в этом ответе:

  • Соответствие указывает результат запуска шаблона RegEx для вашей строки следующим образом: someString.match(regexPattern).
  • Совпадающие шаблоны обозначают все совпадающие части входной строки, которые находятся внутри массива match . Это все экземпляры вашего шаблона внутри входной строки.
  • Совпадающие группы указывают на все группы для перехвата, определенные в шаблоне RegEx. (Шаблоны в скобках, например, так: /format_(.*?)/g, где (.*?) будет сопоставленной группой.) Они находятся в сопоставленных шаблонах .

Описание

Чтобы получить доступ к сопоставленным группам , в каждом из сопоставленных шаблонов вам нужна функция или нечто подобное для итерации по соответствию . Есть много способов сделать это, как показывают многие другие ответы. Большинство других ответов используют цикл while для итерации по всем соответствующим шаблонам , но я думаю, что мы все знаем о потенциальных опасностях при таком подходе. Необходимо сопоставить new RegExp(), а не только сам шаблон, который упоминался только в комментарии. Это связано с тем, что метод .exec() ведет себя подобно генератору - , он останавливается при каждом совпадении , но сохраняет .lastIndex, чтобы продолжить оттуда на следующем .exec() звонок.

Примеры кода

Ниже приведен пример функции searchString, которая возвращает Array всех соответствующих шаблонов , где каждый match является Array со всеми содержащими соответствующими группами . Вместо того, чтобы использовать цикл while, я привел примеры, использующие как функцию Array.prototype.map(), так и более производительный способ - использование простого for -loop.

Краткие версии (меньше кода, больше синтаксического сахара)

Они менее производительны, поскольку они в основном реализуют forEach -циклоп, а не более быстрый for -циклоп.

// Concise ES6/ES2015 syntax
const searchString = 
    (string, pattern) => 
        string
        .match(new RegExp(pattern.source, pattern.flags))
        .map(match => 
            new RegExp(pattern.source, pattern.flags)
            .exec(match));

// Or if you will, with ES5 syntax
function searchString(string, pattern) {
    return string
        .match(new RegExp(pattern.source, pattern.flags))
        .map(match =>
            new RegExp(pattern.source, pattern.flags)
            .exec(match));
}

let string = "something format_abc",
    pattern = /(?:^|\s)format_(.*?)(?:\s|$)/;

let result = searchString(string, pattern);
// [[" format_abc", "abc"], null]
// The trailing `null` disappears if you add the `global` flag

Версии Performant (больше кода, меньше синтаксического сахара)

// Performant ES6/ES2015 syntax
const searchString = (string, pattern) => {
    let result = [];

    const matches = string.match(new RegExp(pattern.source, pattern.flags));

    for (let i = 0; i < matches.length; i++) {
        result.push(new RegExp(pattern.source, pattern.flags).exec(matches[i]));
    }

    return result;
};

// Same thing, but with ES5 syntax
function searchString(string, pattern) {
    var result = [];

    var matches = string.match(new RegExp(pattern.source, pattern.flags));

    for (var i = 0; i < matches.length; i++) {
        result.push(new RegExp(pattern.source, pattern.flags).exec(matches[i]));
    }

    return result;
}

let string = "something format_abc",
    pattern = /(?:^|\s)format_(.*?)(?:\s|$)/;

let result = searchString(string, pattern);
// [[" format_abc", "abc"], null]
// The trailing `null` disappears if you add the `global` flag

Мне еще предстоит сравнить эти альтернативы с альтернативами, упомянутыми ранее в других ответах, но я сомневаюсь, что этот подход менее эффективен и менее надежен, чем другие.

12 голосов
/ 19 июня 2017

Нет необходимости вызывать метод exec! Вы можете использовать метод "match" прямо в строке. Только не забывайте скобки.

var str = "This is cool";
var matches = str.match(/(This is)( cool)$/);
console.log( JSON.stringify(matches) ); // will print ["This is cool","This is"," cool"] or something like that...

Позиция 0 содержит строку со всеми результатами. Позиция 1 имеет первое совпадение, представленное круглыми скобками, а позиция 2 имеет второе совпадение, изолированное в ваших круглых скобках. Вложенные скобки сложны, так что будьте осторожны!

7 голосов
/ 12 июля 2014

Один вкладыш, который практичен, только если у вас есть одна пара круглых скобок:

while ( ( match = myRegex.exec( myStr ) ) && matches.push( match[1] ) ) {};
6 голосов
/ 08 марта 2019

String#matchAll (см. Черновик Этап 3 / предложение от 7 декабря 2018 года ) упрощает доступ ко всем группам в объекте сопоставления (учтите, что Группа 0 - это полное совпадение, в то время как другие группы соответствуют группам захвата в шаблоне):

Имея matchAll, вы можете избежать цикла while и exec с /g ... Вместо этого, используя matchAll, вы получите итератор, который вы можете использовать с более удобным for...of, распространение массива или Array.from() конструкций

Этот метод дает вывод, аналогичный Regex.Matches в C #, re.finditer в Python, preg_match_all в PHP.

См. Демонстрацию JS (протестировано в Google Chrome 73.0.3683.67 (официальная сборка), бета-версия (64-разрядная версия)):

var myString = "key1:value1, key2-value2!!@key3=value3";
var matches = myString.matchAll(/(\w+)[:=-](\w+)/g);
console.log([...matches]); // All match with capturing group values

console.log([...matches]) показывает

enter image description here

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

let matchData = "key1:value1, key2-value2!!@key3=value3".matchAll(/(\w+)[:=-](\w+)/g)
var matches = [...matchData]; // Note matchAll result is not re-iterable

console.log(Array.from(matches, m => m[0])); // All match (Group 0) values
// => [ "key1:value1", "key2-value2", "key3=value3" ]
console.log(Array.from(matches, m => m[1])); // All match (Group 1) values
// => [ "key1", "key2", "key3" ]

ПРИМЕЧАНИЕ : Подробнее о совместимости браузера Подробнее.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...