uhyohyo.net

JavaScript初級者から中級者になろう

四章第二回 正規表現

このページの最終更新日:

今回紹介するのは正規表現です。やや難しいですが面白い機能ですからぜひマスターしましょう。

正規表現とは

正規表現の基本的な機能は文字列を検索することです。前回indexOfというもので文字列を検索できることを解説しましたが、あれのように「ある文字列が含まれているかどうか」だけを調べるのは最も単純な部類の検索です。

正規表現を使うと、もっと複雑な検索条件を指定することができます。

正規表現の書き方

正規表現を検索のために使う場合、正規表現を書くというのは検索条件を書くことに相当します。複雑な検索条件が書けると述べましたが、さすがに日本語や英語で書いてもコンピューターに伝わらないので、専用の書き方があります。それが正規表現です。例えば、

abc

これは、正規表現のもっとも簡単な形です。ただ「abc」とあるだけですが、この正規表現は「"abc"である」という条件を表しています。これくらいの検索ならindexOfでもできますね。

次に、

a.c

という形です。この正規表現中に登場する「.」は特別な意味を持つ文字です。

これはどんな文字でも当てはまるという意味を持ちます。「.」の部分に当てはまる文字は、何でもよいのです(例外的に、改行文字だけはこれに当てはまりません)。

つまり、「aac」も「abc」も、「aあc」「a(c」なども条件に合うことになります。

ちなみに、条件に合うことをマッチするといいます。

次の例を見ましょう。

ab+c

新しい記号+が登場しました。これも特別な記号です。

この記号の意味は、直前の文字が1つ以上繰り返しているです。直前の文字は、今回の場合bですね。1つ以上ということは、数を限定しないのです。つまり、1つ以上なら何個でもいいということです(1つしかなくても「1回繰り返している」ことにします)。ここがポイントです。

つまり、この条件は、「abc」にマッチするほか、「abbc」「abbbc」「abbbbc」「abbbbbc」……にもマッチします。

また、似たようなものに*記号があります。この記号は1つ以上ではなく0個以上を表します。つまり、直前の文字が0個でもいい(無くてもいい)のです。例えば、

ab*c

この正規表現は、ab+cの場合に加えてbが1個もない形である「ac」にもマッチします。

次の例のように、今紹介した2つを組み合わせた形もありえます。

a.+c

この場合、+の直前の文字は.です。この.は、どんな文字でも当てはまるものでした。+によって.が何個でも良くなったということは、この正規表現は、aとcの間に何がどれだけ入っていてもいいということになります。

ただし+は「1つ以上」なので、まったく何も入っていない「ac」という形はマッチしません。しかし、それ以外なら、「abc」も「a1234c」も「aあいうえおc」「akjijgifc」でも、aとcの間に何かが入っている形であればなんでもマッチします。もちろん、a.*cならば「ac」にもマッチします。

さて、まだまだ書き方はあります。

a[abcde]c

という書き方があります。[ 〜 ]まででひとかたまりに扱います。

この意味は、その中に含まれている文字のうちのどれかということになります。中に含まれている文字とは、「abcde」の部分です。

つまり、「aまたはbまたはcまたはdまたはe」ということになるから、この正規表現は、「aac」「abc」「acc」「adc」「aec」のいずれかにマッチします。

また、aからeは連続したアルファベットだから、次のように省略することができます。(連続というのは、正確には文字コードが連続しているということです。)

a[a-e]c

aとeの間のハイフンのおかげで、「a-e」が「aからe」という意味になります。これを利用すると、例えば[a-z]は「aからz」だから小文字のアルファベット全てにマッチします。

また、ハイフンで繋いだ組は次のように複数書くことができます。

[a-zA-Z]

この場合、「aからzとAからZ」という意味になり、アルファベット全てになります。

また、[^abcde][^a-zA-Z]のように、中の先頭に^をつけると意味が変わり、「その中の文字以外の文字のどれか」ということになります。意味が逆になりますね。

JavaScriptで正規表現

さて、正規表現にはまだまだバリエーションがありますが、ここで一度休憩して、実際にJavaScriptで正規表現を扱ってみたいと思います。

正規表現オブジェクト

JavaScriptでは、正規表現(検索条件)はオブジェクトの形で表現します。それが正規表現オブジェクト(RegExpオブジェクト)です。

正規表現を作るには次のように書きます。

new RegExp("ab+c")

見てわかるように、正規表現を文字列で指定します。

しかし、この方法よりも正規表現リテラルを用いるほうが一般的です。それは、

/ab+c/

のような書き方です。ab+cの部分に好きな正規表現を入れてください。1つ目の方法との違いとしては、正規表現リテラルを用いる場合は文字列ではないので""で囲む必要がないという点があります。

文字列にマッチさせる

さて、ある文字列に正規表現がマッチするかどうか調べるためには、次のようにします。すなわち、正規表現オブジェクトの持つtestメソッドを用います。

正規表現オブジェクト.test(文字列)

このtestメソッドの戻り値は真偽値で、マッチすればtrue、しなければfalseを返します。

また、文字列を正規表現にマッチさせるという場合、文字列全体ではなくどこか一部分でも条件を満たしていればマッチします。

ひとつ例を見てみましょう。

if( /bcd/.test("abcde") ){
    console.log("マッチしました");
}else{
    console.log("マッチしませんでした");
}

これでは、文字列"abcde"が、正規表現/bcd/にマッチするかどうか判定しています。これはマッチしているので、trueが返り、if文の中が実行されます。abcdeの部分がマッチしたわけです。

なお、/bcd/はオブジェクトなので、当然次のように変数に入れて利用することもできます。


var cond = /bcd/;
var str = "abcde";
console.log(cond.test(str));

正規表現の書き方2

では、正規表現の書き方の続きを解説します。まずは次の正規表現を見てください。

ab{4}c

ここで、{4}が新しい書き方です。これは「直前の文字を4回繰り返す」というもので、なぜ4回かといえば、当然、{ }の中が4だからです。一般化すると、{n}という指定は、「直前の文字をn回繰り返す」というようになります。nには0以上の整数を入れることができます。

今回の場合、直前の文字はbだから、これは「abbbbc」という文字列にマッチすることになります+*も繰り返し1を表す記号ですが、あちらは繰り返す回数に制限がないのが大きな特徴でした。こちらは、回数を指定することができます。

また、もう少し幅を持たせて、次のような表現が可能です。

ab{2,5}c

今度は、{ 〜 }の中の数値が,で区切られて2つになりました。

{m,n}のように2つの数値を区切って書いたとき、直前の文字をm回以上n回以下繰り返した文字列にマッチします。

上の書き方は4回以外はマッチしませんでしたが、今回はもう少し幅があって、2回以上5回以下となっています。つまり、「abbc」「abbbc」「abbbbc」「abbbbbc」にマッチします。

もう1つあって、それは次のような書き方です。

ab{2,}c

前と同じように2つの数値を区切って書く書き方かと思いきや、2つめの数値が省略されています。この書き方は、繰り返しの最低回数は指定するが、最高回数は制限しないという場合の方法です。

つまり、今回の場合「2回以上」ということになり、「abbc」「abbbc」「abbbbc」・・・にマッチします。お気づきかとは思いますが、{1,}と書けば+と同じになり、{0,}と書けば*と同じになります。

これでこのシリーズは終了です。さらに、

ab?c

という書き方ができます。この「?」は、直前の文字が0回または1回という意味です。つまり、「ac」か「abc」にマッチします。

グループ化

重要な機能としてグループ化というものがあります。これは、複数の文字をひとまとめに扱うことができるものです。グループ化は、

a(bc)

のように、ひとまとめにしたい部分を( 〜 )で囲みます。また、(?: 〜 )で囲むこともできます。この2つの違いは次回紹介しますが、基本的には後者の(?: 〜 )を使います。

これだけでは何も変わりませんが、これを例えば次のように利用できます。

a(?:bc)+

+は直前の文字を1回以上繰り返すというものでした。ここで、+の直前にあるのは文字ではなく、(?:bc)というグループです。これはbcをひとまとめにしたグループなので、「bc」を繰り返すということになります。

したがって、「abc」「abcbc」「abcbcbc」「abcbcbcbc」…… にマッチするということになります。

+以外にも、*{〜}?などでも同じです。今まで「文字を繰り返す」と説明したものは、グループを繰り返すのにも使えます。

さて、次のような書き方もあります。

aaa|abc

aaaabc|で区切られています。これは単純に「aaaabcどちらか」という意味です。この正規表現でマッチさせる場合、aaaかabcのどちらかが含まれていればマッチします。

aaa|abc|zzzzzのように、3つ以上候補をたてることも可能です。

さて、これを他の書き方に組み込むとどうなるでしょう。例えば、1234の後にaaa|abc|zzzzz(aaaかabcかzzzzzのどれか)が続く場合、

1234aaa|abc|zzzzz

のように書くと、「1234aaaabczzzzz」という意味になってしまいます。

これは先述のグループ化を使うことで正しく書くことができます。すなわち、次のようにします。

1234(?:aaa|abc|zzzzz)

これは、「1234aaa」「1234abc」「1234zzzzz」のいずれかにマッチします。

1文字である種類の文字のどれかを表す特殊な文字

\s

という書き方があります。ここで、\sは2文字でセットです。一般に、「\」は、その次の文字と合わさって特殊な意味を持ちます。

それでは、この\sはどういう意味を持つかというと、空白文字にマッチします。空白文字は、文字通り空白のことで、半角スペース、タブ、改行など、空白を表す文字が当てはまります。例として、次の正規表現は"foo bar""foo \t bar"などにマッチします。

foo\s+bar

他にも、\dというのもあります。今度はsではなくdです。これは、数字1文字です。数字とは0から9のことなので、\d[0-9]と同じ意味になります。これも結構使う機会が多いです。

ここで、今まで特殊な意味の文字がいろいろ出てきました。. + * ? { } ( ) [ ] | \などです。それでは、これらの文字を、特殊な意味ではなく、これらの文字そのものが含まれるという意味で使いたい場合には、どうすればいいのでしょうか。

実はその場合は\. \+ \*などのように、前に\をつけます。これは覚えておきましょう。例えば、3\+2という正規表現の場合、"3+2"という文字列にマッチします。

気をつけていただきたいのは、このような\を用いる記法は前回紹介したエスケープシーケンスとは異なる概念であるということです。今回のは正規表現の記法であるのに対し、前回のは文字列リテラル中での記法です。ただし、\u1234のようなコードポイントを指定するエスケープシーケンスは、正規表現でも使うことができます。

位置を指定する文字

今まで、書いたものはみんなそれぞれ文字にマッチしてきました。別な種類の特殊文字としては、文字にマッチするのではなく位置を指定するための特殊な文字もあります。

例として、次の正規表現を見てください。

^abc

先頭に^がついてます。実は、この文字はその位置が文字列の先頭であることを示します。(正規表現に複数行フラグがついている場合、行頭を表します。複数行フラグについては後々紹介します。)

つまり、この正規表現はabcという文字列にマッチしますが、abcの直前が文字列の先頭なので、この正規表現は文字列の先頭にあるabcのみマッチします。

だから、同じabcでも、

1234abc
    ^^^

aabcdefg
 ^^^

にはマッチせず、

abcdefgh
^^^

にはマッチするということになります。実際に、/^abc/.test("1234abc")/^abc/.test("abcdefgh")などを実行して試してみましょう。/abc/.test("1234abc")の場合との違いに注意してください。

また、同じように文字列の末尾を表す特殊文字もあります。それは$です。

例えば、abc$の場合、

aaaabcdefg
   ^^^

abcdefg
^^^

にはマッチせず、

aaabc
  ^^^

にはマッチするということです。

これらを組み合わせて、

^abc$

とするとどうでしょう。abcが最初であり最後であるということだから、

abcdef
^^^
aaaaabc
    ^^^

にはマッチせず、

abc
^^^

という、文字列が完全にそれと一致する場合のみマッチします。

最後に、 \S\Dを紹介します。先ほど紹介した\s\dの大文字バージョンですが、大文字になると意味が逆転します。すなわち、\Sは「\s以外」、\Dは「\d以外」にマッチします。たまに使います。

具体的には、\Sは「空白文字以外の任意の文字」、\Dは「数字以外の任意の文字」にマッチすることになります。

\Sが使われる場面はいろいろありますが、面白いのは次のような使い方です。[\s\S]

これは、「複数の文字のうちいずれか」を表す[ 〜 ]の中に\s\Sが入った形です。このように、\sなどを[ 〜 ]に入れることが可能です。この場合、ちゃんと「\sまたは\S」という意味になります。

\sは空白で\Sは空白以外だから、2つ合わせると「全ての文字」という意味になります。全ての文字という意味の記号として.を紹介しましたが、[\s\S].よりも範囲が広いです。なぜなら、.は改行文字にマッチしませんが、[\s\S]は改行文字にもマッチするからです。(\sが改行文字にマッチするため。)これは逆の意味を持つ2つの記号なら何でもよく、[\d\D]などでも同じことができます。また、.が改行にマッチしないのを補えばいいので(?:.|\s)でもできます。ただ、よく使われるのは[\s\S]のような気がします。

以上で正規表現の書き方は終わりです。ここではよく使いそうなものは解説しましたが、他にも記法があります。自分で調べてみましょう。

次回はJavaScriptから正規表現を活用する方法を紹介します。