1. ホーム
  2. javascript

[解決済み] JavaScriptの正規表現でマッチしたグループにアクセスするにはどうすればよいですか?

2022-03-20 06:01:57

質問

を使って文字列の一部にマッチさせたい。 正規表現 で括られた部分文字列にアクセスします。

    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で、マッチした部分文字列が "undefined"を返すのはなぜですか? .


という問題がありました。 console.log のようにパラメータを受け取ります。 printf ステートメントを使用しており、私がログを記録していた文字列 ( "%A" には特殊な値があり、次のパラメータの値を探そうとしていました。

解決方法は?

このようにキャプチャリンググループにアクセスすることができます。

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

また、複数のマッチングがある場合は、それらを繰り返し処理することができます。

var myString = "something format_abc";
var myRegexp = new RegExp("(?:^|\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)
}

イテレータを返すので、遅延処理と言える。これは、特に大量のキャプチャグループや非常に大きな文字列を扱う場合に有効である。しかし、もし必要なら、結果は スプレッド構文 または 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]);
}

とりあえず、この提案がもっと広く支持されるようになるまでの間は 公式shimパッケージ .

また、このメソッドの内部動作はシンプルです。ジェネレーター関数を使った同等の実装は以下のようになる。

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の質問でさえも、参照されたのは 提案の議論 .