MENU

正規表現

【JavaScript】 正規表現match()とexec()の違い

更新日:2022/09/07

JavaScriptには、正規表現を使って文字列を検索するメソッドが二つあります。
match()とexec()です。

 

この二つは何が違うのでしょうか?
気になったので調べてみました。

 

 

JavaScriptの正規表現については、こちらを参照してください。
【JavaScript】 正規表現まとめメモ

 

関連記事:
【JavaScript】 matchとmatchAllの違い

 


こちらの自作ツールもよろしくお願いします。

 

2022/9 内容を少し整理しました

match()とexec()の書式

 

まずは、match()とexec()の書式を確認してみます。

 

match()の書式


文字列.match( regexp );

 

regexpは、正規表現オブジェクトです。
match()は、「文字列を正規表現オブジェクトを使ってマッチ処理する」という意味合いになります。

 

exec()の書式


regexp.exec( 文字列 );

 

execはexecuteの略で、実行するという意味があります。
exec()は、「正規表現オブジェクトが、文字列に対して正規表現を実行する」という表現になります。

 

各メソッドの書式から見てわかるように、match()は、Stringオブジェクト(文字列オブジェクト)が持つメソッドです。
一方exec()は、RegExpオブジェクト(正規表現オブジェクト)が持つメソッドです。

 

つまり、所属しているオブジェクトが異なります。

同じ点 : オプションに『g』がない場合結果が同じになる

 

exec()とmatch()の同じ点を見ていきます。

 

正規表現オブジェクト作成時に、オプション『g(グローバルサーチ)』を指定しないと、exec()とmatch()の結果が同じになります。

 

オプション『g』:指定しない場合一度マッチングしたら終了。

 

オプションに『g』がないときのexec()とmatch()の挙動

 

オプションに『g』がないとき、exec()とmatch()は最初に一致した文字列を配列で返します。

 

例:


const text = "123abc#$%abc";
const rg = /abc/;
const result = rg.exec( text );  // または text.match( rg );

 

上のコードはexec()を実行していますが、match()を実行しても同じ結果になります。

 

結果:

 

result : Array{
        0 : "abc"
        index : 3
        input : "123abc#$%abc"
        length : 1
    }

 

通常Arrayオブジェクトは、indexとinputというプロパティを持っていません。
しかしexec()とmatch()の結果として返された場合、この二つのプロパティが付加されます。

 

index : 検索された文字列の位置(0~)
input : 検索元の文字列

 

オプションに『g』がないときパターンで( )を使用したときの挙動

 

正規表現パターン内で『( )』を使用すると、対応する文字列が保存(キャプチャ)されます。
保存された文字列は、結果配列のインデックス1以降にセットされます。

 

例:


const text = "123abc#$%abc";
const rg = /a(bc)/;
const result = rg.exec( text );  // または text.match( rg );

 

結果を見ると、インデックス1にマッチした文字がセットされているのがわかります。

 

結果:

 

result : Array{
        0 : "abc"
        1 : "bc"
        index : 3
        input : "123abc#$%abc"
        length : 2
    }

 

今度は『( )』を2回使用してみます。

 

例:


const text = "123abc#$%abc";
const rg = /(a)(bc)/;
const result = rg.exec( text );  // または text.match( rg );

 

結果を見ると、インデックスが2に増えていますね。

 

結果:

 

result : Array{
        0 : "abc"
        1 : "a"
        2 : "bc"
        index : 3
        input : "123abc#$%abc"
        length : 3
    }

 

違う点 : オプションに『g』がある場合結果が変わる

 

次は、exec()とmatch()の異なる点です。

 

正規表現オブジェクト作成時、オプション『g(グローバルサーチ)』を指定すると、exec()とmatch()は異なる結果を返します。

 

オプションに『g』があるときexec()はループ処理が必要

 

まずはexec()から見ていきます。

 

exec()は、最初に見つかった文字列及びキャプチャを配列にセットして返します。
同時に正規表現オブジェクト内のlastIndexプロパティに、どこまで検索されたのかを記憶するために、元の文字列の位置がセットされます。

 


const text = "123a1bc#$%a2bc";
const rg = /(a\d)(bc)/g;
const result = rg.exec( text );

 

実行すると、最初に一致する文字の情報が返ります。
これは、『g』がないとき同じです。

 

結果:

 

result : Array{
        0 : "abc"
        1 : "a1"
        2 : "bc"
        index : 3
        input : "123a1bc#$%a2bc"
        length : 3
    }

 

このとき、正規表現オブジェクトのlastIndexプロパティに、検索が終わった位置が記憶されています。

 

"123a1bc#$%a2bc"の最初の"bc"まで検索しているので、7になっています。

 

rg.lastIndex : 7

 

残りの文字列を取得するには、結果がnullになるまでループさせます。

 


let result;
while( (result  = rg.exec( text )) !== null ){
            console.log( result , rg.lastIndex );
}

 

これで全ての結果が得られます。

 

■よくやる間違いコード

 

僕がexec()について、よくわかっていなかった頃にやった失敗を挙げておきます。

 

次のようなコードです。

 


let result;
while( (result  = /(a\d)(bc)/g.exec( text )) !== null ){
           ・・・・処理
}

 

ループ内で正規表現リテラルを使用しています。
ソースの行数が減るから、こちらの方がいいような気がしますね。

 

しかし、実行すると無限ループです。

 

ループのたびに正規表現オブジェクトが生成されて、文字列が最初からマッチングされます。
結果が null にならないので、ループが終わらないのです。
怖いですね…

 

 

オプションに『g』があるときmatch()はキャプチャを無視する

 

次はmatch()です。

 

オプションに『g』があるときmatch()は、全てのマッチした文字を配列にセットします。
ただし、キャプチャは保存されません。

 


const text3 = "123a1bc#$%a2bc";
const rg3 = /(a\d)(bc)/g;
const result = text3.match( rg3 )

 

上のコードの正規表現は、a数字bc にマッチします。
"123a1bc#$%a2bc"なら"a1bc"と"a2bc"ですね。

 

そのため、結果は次のようになります。

 

結果:

 

result : Array{
0 : "a1bc"
1 : "a2bc"
length : 2
}

 

なお、gオプションが未指定のときにセットされてていた、indexとinput プロパティはセットされません。

まとめ

 

match()とexec()は、gオプションを指定しないなら同じ結果となる。
どちらを使っても問題なさそうです。

 

しかしgオプションをセットした場合は、使い分ける必要があります。

 

match() : キャプチャを取得しない場合、こちらを使用。

 

exec() : キャプチャを取得する場合、こちらを使用。

 

キャプチャを取得しない場合でもexec()を使用できますが、ループ処理が必要なので面倒です。
match()の方が楽ですね。

 

キャプチャを取得する場合は、exec()一択です。

スポンサーリンク

記事の内容について

こんにちはけーちゃんです。
説明するのって難しいですね。

「なんか言ってることおかしくない?」
たぶん、こんなご意見あると思います。

裏付けを取りながら記事を作成していますが、僕の勘違いだったり、そもそも情報源の内容が間違えていたりで、正確でないことが多いと思います。
そんなときは、ご意見もらえたら嬉しいです。

掲載コードについては事前に動作確認をしていますが、貼り付け後に体裁を整えるなどをした結果動作しないものになっていることがあります。
生暖かい視線でスルーするか、ご指摘ください。

ご意見、ご指摘はこちら。
https://note.affi-sapo-sv.com/info.php

 

OFUSEを設置してみました。
応援いただけると嬉しいです。