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


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

13 ответов


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

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

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

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

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


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

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

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

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 () для вряд ли что-либо когда-либо снова.


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

и последнее, но не менее важное: я обнаружил, что один код строки, который отлично работал для меня (JS ES6):

var reg = /#([\S]+)/igm; //get hashtags
var string = 'mi alegría es total! ✌\n#fiestasdefindeaño #PadreHijo #buenosmomentos #france #paris';

var matches = (string.match(reg) || []).map(e => e.replace(reg, ''));
console.log(matches);

это вернется : [fiestasdefindeaño, PadreHijo, buenosmomentos, france, paris]


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

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

описание

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

примеры кода

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

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

они менее эффективны, поскольку они в основном реализуют forEach-loop вместо более быстрого 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 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

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


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


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

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

используя ваш код:

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

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


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

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

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

/\bformat_(.*?)\b/

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


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