JavaScript正規表現で一致したグループにどのようにアクセスしますか?


1368

正規表現を使用して文字列の一部を照合し、括弧で囲まれた部分文字列にアクセスします。

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"が未定義であると報告することは非常に奇妙な動作のようですが、この質問とは直接関係がないため、新しい質問を開きました。JavaScriptで一致した部分文字列が "未定義"を返すのはなぜですか?


問題はconsole.log、パラメーターをprintfステートメントのように受け取ることでした。また、ログに記録し"%A"ていた文字列()に特別な値が含まれていたため、次のパラメーターの値を見つけようとしていました。

回答:


1675

次のようにキャプチャグループにアクセスできます。

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

編集:2019-09-10

ご覧のように、複数のマッチを反復する方法はあまり直感的ではありませんでした。これはString.prototype.matchAll方法の提案につながりました。この新しいメソッドは、ECMAScript 2020仕様で出荷される予定です。クリーンなAPIを提供し、複数の問題を解決します。それは主要なブラウザーとJSエンジンにChrome 73+ / Node 12+とFirefox 67+ として上陸し始めました。

このメソッドはイテレータを返し、次のように使用されます。

const string = "something format_abc";
const regexp = /(?:^|\s)format_(.*?)(?:\s|$)/g;
const matches = string.matchAll(regexp);
    
for (const match of matches) {
  console.log(match);
  console.log(match.index)
}

イテレータを返すので、怠惰であると言えます。これは、特に多数のキャプチャグループや非常に大きな文字列を処理する場合に役立ちます。ただし、必要に応じて、spread構文またはArray.fromメソッドを使用して、結果を配列に簡単に変換できます。

function getFirstGroup(regexp, str) {
  const array = [...str.matchAll(regexp)];
  return array.map(m => m[1]);
}

// or:
function getFirstGroup(regexp, str) {
  return Array.from(str.matchAll(regexp), m => m[1]);
}

それまでの間、この提案はより幅広いサポートを得ますが、公式のシムパッケージを使用できます

また、メソッドの内部動作は単純です。ジェネレータ関数を使用した同等の実装は次のようになります。

function* matchAll(str, regexp) {
  const flags = regexp.global ? regexp.flags : regexp.flags + "g";
  const re = new RegExp(regexp, flags);
  let match;
  while (match = re.exec(str)) {
    yield match;
  }
}

元の正規表現のコピーが作成されます。これはlastIndex、複数の一致を通過するときにプロパティの変異による副作用を回避するためです。

また、無限ループを回避するために、正規表現にグローバルフラグがあることを確認する必要があります。

また、このStackOverflowの質問でさえ、提案の議論で参照されたことをうれしく思います。


114
+1 2番目の例では、「/ myregexp /」だけでなくRegExpオブジェクトを使用する必要があることに注意してください。これは、オブジェクトのlastIndex値を保持するためです。Regexpオブジェクトを使用しないと、無限に繰り返されます
ianaz '28

7
@ianaz:信じられない?http://jsfiddle.net/weEg9/は、少なくともChromeで動作するようです。
spinningarrow

16
なぜ、上記の代わりに実行しますかvar match = myString.match(myRegexp); // alert(match[1])
JohnAllen 2013

29
明示的な「新しいRegExp」は必要ありませんが、/ gが指定されていない限り、無限ループが発生します
George C

4
無限ループに陥らないもう1つの方法は、文字列を明示的に更新することです。たとえば、string = string.substring(match.index + match[0].length)
Olga

186

これは、各試合の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);


12
これは、1つだけを取得するのではなく、すべての一致の反復を正しく表示するため、他の回答よりはるかに優れています。
Rob Evans

13
mnnは正しいです。'g'フラグが存在しない場合、これは無限ループを生成します。この機能には十分注意してください。
Druska

4
私はこれをpythonのre.findall()に類似するように改善しました。すべての一致を配列の配列にグループ化します。また、グローバル修飾子の無限ループの問題も修正されています。 jsfiddle.net/ravishi/MbwpV
ravishi

5
@MichaelMikowskiで無限ループを隠しましたが、コードの実行が遅くなります。私は、コードを不正な方法で中断させて、開発でそれをキャッチする方がよいと主張します。いくつかのbsの最大反復回数を割り込むのはずさんです。根本原因を修正するのではなく、問題を隠すことは答えではありません。
ワラサー2014年

4
@MichaelMikowskiは、実行制限に達していない場合は意味のある速度低下にはなりません。あなたがいるとき、それは明らかにはるかに遅いです。私はあなたのコードが機能しないと言っているのではなく、実際にはそれが良いというよりは害を引き起こすと私は言っています。開発環境で作業している人は、コードのチャンクを10,000回不要に実行しても、コードが無負荷で正常に機能することを確認できます。次に、それを本番環境にプッシュし、アプリが負荷でダウンする理由を不思議に思います。私の経験では、物事が明白な方法で、開発サイクルの早い段階で壊れた方がいいです。
ワラサー2014年

58

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

\bまったく同じものではありません。(動作します--format_foo/が動作しませんformat_a_b)しかし、私はあなたの表現の代わりとなるものを示したかったのですが、それは問題ありません。もちろん、match電話は重要です。


2
それはまったく逆です。'\ b'は単語を区切ります。word = '\ w' = [a-zA-Z0-9_]。「format_a_b」は一言です。
BF

1
@BFHonestly、私はformat_a_b6年前の事後考えとして「does n't work on 」を追加しました、そして私がそこで何を意味したのか思い出せません... :-)私はそれが「キャプチャするaだけでは機能しない」ことを意味したと思います、すなわち。の後の最初のアルファベット部分format_
PhiLho 2015

1
「-」と「/」は\ word文字ではないため、\ b(-format_foo /} \ bは「--format_foo /」を返さないと言いたかったのですが、\ b(format_a_b)\ bは「format_a_b」を返します「そうです。私はあなたの文章を丸括弧で参照しています。(反対票は投じませんでした!)
BF

31

上記の複数一致の括弧の例に関して、私が望んでいたものを得られなかった後、私はここで答えを探していました:

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

上記のwhileと.push()を使用した少し複雑な関数呼び出しを調べたところ、代わりにmystring.replace()を使用して問題を非常にエレガントに解決できることがわかりました(置き換えは重要ではなく、実行もされません) 、CLEAN、2番目のパラメーターの組み込みの再帰関数呼び出しオプションは!):

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()を使用するつもりはないと思います。


26

最後に大事なことを言い忘れましたが、私には問題なく動作するコードが1行見つかりました(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']

1
ブーム!これは、ここで最もエレガントなソリューションです。replaceこれはAlexzによる完全なアプローチよりも優れていることがわかりました。これは、先読みが少なく、複数の結果に対してエレガントなためです。セバスチャンH.
コーディ

これは非常にうまく機能するため、間違いなく私の
コーディ

1
@Codyハハありがとう男!
Sebastien H.

19

この回答で使用される用語:

  • 一致は、次のように文字列に対してRegExパターンを実行した結果を示しますsomeString.match(regexPattern)
  • 整合パターンは入力文字列の一致するすべての部分、内部のすべての存在を示すマッチアレイ。これらはすべて、入力文字列内のパターンのインスタンスです。
  • 一致したグループは、RegExパターンで定義された、キャッチするすべてのグループを示します。(括弧内のパターンは、次のようになります:/format_(.*?)/g。ここ(.*?)で、一致したグループになります。)これらは、一致したパターン内にあります

説明

アクセスを取得するにはマッチしたグループを、それぞれにマッチしたパターンは、関数または反復処理するために似たような必要な試合を。他の多くの回答が示すように、これを行うにはいくつかの方法があります。他のほとんどの回答では、whileループを使用してすべての一致したパターンを反復処理します、そのアプローチの潜在的な危険性は誰もが知っていると思います。new RegExp()コメントでのみ言及されたパターンそのものではなく、と照合する必要があります。これは、ある.exec()方法がに似て振る舞うジェネレータ関数 - それは試合があるたびに停止しますが、そのを保つ.lastIndexの次に、そこから継続する.exec()コール。

コード例

以下は、すべての一致したパターンのsearchStringを返す関数の例です。各パターンは、すべての一致したグループを含むです。whileループを使用する代わりに、関数と、より単純なループを使用したよりパフォーマンスの高い方法の両方を使用した例を示しました。ArraymatchArrayArray.prototype.map()for

簡潔なバージョン(より少ないコード、より多くの構文糖)

これらは基本的forEachに、より高速なfor-loopの代わりに-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

これらの選択肢を他の回答で以前に述べたものと比較する必要はありませんが、この方法は他の方法よりもパフォーマンスとフェイルセーフが低いと思います。


19

String#matchAllステージ3ドラフト/ 2018年12月7日の提案を参照)、一致オブジェクト内のすべてのグループへのアクセスを簡略化します(グループ0は完全一致であり、他のグループはパターン内のキャプチャグループに対応しています):

matchAll利用できる、あなたは避けることができるwhileループをしてexec/g...代わりに、使用することによってmatchAll、あなたはより便利に使用することができますイテレータ取り戻すfor...of配列の広がり、またはArray.from()構造を

このメソッドは、Regex.MatchesC#、re.finditerPython、preg_match_allPHP と同様の出力を生成します。

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])ショー

ここに画像の説明を入力してください

を使用して、マッチ値または特定のグループ値を取得することもできます

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" ]

ブラウザの互換性の詳細をご覧ください。


キーと値のペアの完璧な例。簡潔で読みやすく、非常に使いやすい。また、エラー処理が改善され、スプレッドはnullではなく空の配列を返すため、「エラー、nullの "長さ"プロパティはありません」
Jarrod McGuire

17

構文はおそらく保持するのが最善ではありません。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"]

16

execメソッドを呼び出す必要はありません!文字列に対して直接「一致」メソッドを使用できます。括弧を忘れないでください。

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には2番目の一致が括弧で分離されています。ネストされた括弧は扱いにくいので注意してください。


4
グローバルフラグがないと、これはすべての一致を返します。これを使用すると、大きなものは1つしか得られないので、注意してください。
Shadymilkman01 2018

8

括弧が1組ある場合にのみ実用的な1つのライナー:

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

4
なぜだろうwhile (match = myRegex.exec(myStr)) matches.push(match[1])
ウィルマ2017

7

コードを使用する:

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

編集:必要に応じて、Safari 3。


7

es2018 String.match()では、名前付きグループを使用できるようになり、正規表現が何をしようとしているかをより明確にしています。

const url =
  '/programming/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);

そしてあなたは次のようなものを得るでしょう

{プロトコル: "https"、ホスト名: "stackoverflow.com"、パス名: "questions / 432493 / how-do-you-access-the-matched-groups-in-a-javascript-regular-expression"、クエリ文字列: " some = parameter "}


6

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


3

あなたのコードは私(MacではFF3)で動作しますが、私がPhiLoに同意しても、正規表現はおそらく次のようになるはずです:

/\bformat_(.*?)\b/

(もちろん、正規表現のコンテキストがわからないので、わかりません。)


1
これはスペースで区切られたリストなので、\ sで結構です。そのコードが私のために機能していなかったのは奇妙です(FF3 Vista)
nickf 2009年

1
はい、本当に奇妙です。Firebugコンソールで単独で試したことがありますか?そうでなければ空のページからです。
PEZ、

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

2

複数の一致を解析するために明示的なループは実際には必要ありません。次のように、2番目の引数として置換関数を渡します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);

m0引数が完全なマッチサブストリングを表す{0}{1}などm1すなわちある正規表現の括弧で囲まれた部分最初に一致した基を表し、0最初の一致のために。そしてposition、一致するグループが見つかった文字列内の開始インデックスです。この場合は未使用です。


1

正規表現で一致したグループにアクセスするには、バックスラッシュとそれに続く一致するグループの番号を使用します。

/([a-z])\1/

最初のグループ([az])と一致して表されるコード\ 1


1

1行のソリューション:

const matches = (text,regex) => [...text.matchAll(regex)].map(([match])=>match)

したがって、この方法を使用できます(/ gを使用する必要があります):

matches("something format_abc", /(?:^|\s)format_(.*?)(?:\s|$)/g)

結果:

[" format_abc"]


0

私はあなたが私のようなものであり、正規表現が次のようなオブジェクトを返すことを望みます:

{
    match: '...',
    matchAtIndex: 0,
    capturedGroups: [ '...', '...' ]
}

次に、関数を下から切り取ります

/**
 * @param {string | number} input
 *          The input string to match
 * @param {regex | string}  expression
 *          Regular expression 
 * @param {string} flags
 *          Optional Flags
 * 
 * @returns {array}
 * [{
    match: '...',
    matchAtIndex: 0,
    capturedGroups: [ '...', '...' ]
  }]     
 */
function regexMatch(input, expression, flags = "g") {
  let regex = expression instanceof RegExp ? expression : new RegExp(expression, flags)
  let matches = input.matchAll(regex)
  matches = [...matches]
  return matches.map(item => {
    return {
      match: item[0],
      matchAtIndex: item.index,
      capturedGroups: item.length > 1 ? item.slice(1) : undefined
    }
  })
}

let input = "key1:value1, key2:value2 "
let regex = /(\w+):(\w+)/g

let matches = regexMatch(input, regex)

console.log(matches)


0

RegExp。$ 1 ... $ n番目のグループを使用するだけです。例:

1. 1番目のグループのRegExpと一致する。$ 1

  1. 2番目のグループのRegExpに一致させるには$ 2

あなたが正規表現のように3つのグループを使用する場合(string.match(regex)の後に使用することに注意してください)

RegExp。$ 1 RegExp。$ 2 RegExp。$ 3

 var str = "The rain in ${india} stays safe"; 
  var res = str.match(/\${(.*?)\}/ig);
  //i used only one group in above example so RegExp.$1
console.log(RegExp.$1)

//easiest way is use RegExp.$1 1st group in regex and 2nd grounp like
 //RegExp.$2 if exist use after match

var regex=/\${(.*?)\}/ig;
var str = "The rain in ${SPAIN} stays ${mainly} in the plain"; 
  var res = str.match(regex);
for (const match of res) {
  var res = match.match(regex);
  console.log(match);
  console.log(RegExp.$1)
 
}

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.