Как получить доступ к сопоставленным группам в регулярном выражении JavaScript?

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

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 кажется очень странным поведением, но оно напрямую не связано с этим вопросом, поэтому я открыл новый, Почему совпадающая подстрока, возвращающая "undefined" в JavaScript?.


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

Ответы

Ответ 1

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

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

Ответ 2

Вот метод, который вы можете использовать для получения 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);

Ответ 3

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

Ответ 4

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

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

Посмотрев на несколько запутанные вызовы функций с while и .push() выше, мне стало ясно, что проблема может быть решена очень элегантно с помощью mystring.replace() вместо этого (замена не является точкой, а isn ' t даже сделано, функция 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(), чтобы больше ничего не сделать.

Ответ 5

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

Ответ 6

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

  • Соответствие указывает результат запуска вашего шаблона 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 -loop вместо более быстрого for -loop.

// 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 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

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

Ответ 7

Ваш синтаксис, вероятно, не лучший. 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"]

Ответ 8

Нет необходимости вызывать метод 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 имеет второе совпадение, выделенное в ваших круглых скобках. Вложенные круглые скобки сложны, поэтому будьте осторожны!

Ответ 9

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

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

Этот метод выдает результаты, аналогичные Regex.Matches в С#, re.finditer в Python, preg_match_all в PHP.

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

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

Ответ 10

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

while ( ( match = myRegex.exec( myStr ) ) && matches.push( match[1] ) ) {};

Ответ 11

Использование вашего кода:

console.log(arr[1]);  // prints: abc
console.log(arr[0]);  // prints:  format_abc

Изменить: Safari 3, если это имеет значение.

Ответ 12

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 = 'Rs.200 is Debited to A/c ...2031 on 02-12-14 20:05:49 (Clear Bal Rs.66248.77) AT ATM. TollFree 1800223344 18001024455 (6am-10pm)';
var myRegEx = /clear bal.+?(\d+\.?\d{2})/gi;

// 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);

Ответ 13

С es2018 вы можете теперь String.match() с именованными группами, делает ваше регулярное выражение более явным из того, что он пытался сделать.

const url =
  'https://stackoverflow.com/questions/432493/how-do-you-access-the-matched-groups-in-a-javascript-regular-expression?some=parameter';
const regex = /(?<protocol>https?):\/\/(?<hostname>[\w-\.]*)\/(?<pathname>[\w-\./]+)\??(?<querystring>.*?)?$/;
const { groups: segments } = url.match(regex);
console.log(segments);

и вы получите что-то вроде

{protocol: "https", имя хоста: "stackoverflow.com", имя пути: "questions/432493/how-do-you-you-access-the-matched-groups-in-a-javascript-регулярное выражение", строка запроса: " некоторые = параметр "}

Ответ 14

Ваш код работает для меня (FF3 на Mac), даже если я согласен с PhiLo, что регулярное выражение должно быть:

/\bformat_(.*?)\b/

(Но, конечно, я не уверен, потому что не знаю контекста регулярного выражения.)

Ответ 15

/*Regex function for extracting object from "window.location.search" string.
 */

var search = "?a=3&b=4&c=7"; // Example search string

var getSearchObj = function (searchString) {

    var match, key, value, obj = {};
    var pattern = /(\w+)=(\w+)/g;
    var search = searchString.substr(1); // Remove '?'

    while (match = pattern.exec(search)) {
        obj[match[0].split('=')[0]] = match[0].split('=')[1];
    }

    return obj;

};

console.log(getSearchObj(search));

Ответ 16

Вам на самом деле не нужен явный цикл для анализа нескольких совпадений - передайте функцию замены в качестве второго аргумента, как описано в: String.prototype.replace(regex, func):

var str = "Our chief weapon is {1}, {0} and {2}!"; 
var params= ['surprise', 'fear', 'ruthless efficiency'];
var patt = /{([^}]+)}/g;

str=str.replace(patt, function(m0, m1, position){return params[parseInt(m1)];});

document.write(str);

Ответ 17

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

/([a-z])\1/

В коде \1 представлено соответствие первой группе ([az])

Ответ 18

Посмотрите эту ссылку на уроке 12:

Вы можете группировать символы, используя специальные ( и ) (круглые скобки). Чтобы записать файл изображения, напишите выражение ^(IMG(\d+))\.png$.

Ответ 19

function get_transaction_type_medium(sms_body) {
    if (sms_body.toLowerCase().includes("is debited to a/c") && sms_body.toLowerCase().includes("at atm")) {
        return ["ATM", "M"];
    }
    else {
        return [null, null];
    }
}

Ответ 20

Получить все вхождения группы

let m=[], s = "something format_abc  format_def  format_ghi";

s.replace(/(?:^|\s)format_(.*?)(?:\s|$)/g, (x,y)=> m.push(y));

console.log(m);