uhyohyo.net

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

四章第二回 正規表現

正規表現は、とても面白い機能です。さらに文字列操作の幅が広がります。

正規表現とは

正規表現は、基本的には文字列を検索するために使うものです。前回indexOfというもので文字列を検索できることを解説しましたが、あれは、ただある文字列が含まれているかどうかだけを検索するという、単純なものです。

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

正規表現の書き方

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

abc

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

次に、

a.c

という形です。「b」が「.」に変わりました。

同じように"a.c"という文字列であるという条件かと思いきや、そうではありません。ここで、.特別な意味を持つ文字です。

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

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

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

さて、幅はもっともっと広がります。

ab+c

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

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

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

また、似たようなものに

ab*c

というものがあります。「+」が「*」に変わりました。この記号は、1つ以上ではなく「0個以上」を表します。つまり、直前の文字が0個でもいい(無くてもいい)のです。つまり、この正規表現は、+の場合に加えて、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オブジェクト)です。

正規表現オブジェクトを作るには、特別な書き方があります。それは、

/正規表現/

という書き方です。「正規表現」の部分は、「ab+c」とか「a[a-e]c」とかの条件の部分です。"で囲むと文字列になりますが、そのかわりに/で囲むと正規表現オブジェクトを表すのです。

このような書き方は、[ 〜 ]で配列を作ったり、{ 〜 }で普通のオブジェクトを作るのと似ています。

文字列にマッチさせる

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

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

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

また、文字列を正規表現にマッチさせという場合、文字列のどこか一部分が条件を満たしていればマッチします。

いくつか例を見てみましょう。

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

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

abcde
 ‾‾‾

の部分がマッチしたわけです。

正規表現の書き方

再び、正規表現の書き方を解説していきます。

ab{4}c

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

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

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

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」・・・にマッチします。

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

ab?c

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

グループ化

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

a(bc)

のようにします。ひとまとめにしたい部分を、( 〜 )で囲むだけです。

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

a(bc)+

+は直前の文字を1回以上繰り返すというものでした。ここで、直前の文字は、cというわけではなくなります。( 〜 ) でbcをひとまとめにして扱ったから、「bc」を繰り返すということになります。

従って、「abc」「abcbc」「abcbcbc」「abcbcbcbc」・・・ にマッチするということになります。

+以外にも、*や{〜}や?などでも同じです。

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

aaa|abc

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

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

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

1234aaa|abc|zzzzz

のように書くと、「『1234aaa』か『abc』か『zzzzz』」という意味になってしまいます。

ここで、上のグループ化が登場します。

1234(aaa|abc|zzzzz)

のように書くことで、正しくマッチします。見た目にも1234とaaa|abc|zzzzzが分離していることがわかります。

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

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

\s

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

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

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

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

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

位置を指定する文字

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

^abcという書き方があります。先頭に^がついていて、実は、この文字は、その位置が行頭であることを示します。文字列が1行だけなら(1行のことが多いですが)、文字列の先頭であることを表します。

今回検索する文字列が1行だとすると、先頭の直後にabcがあるから、abcで始まる文字列にマッチします。

だから、同じabcでも、

1234abc
    ‾‾‾
          

aabcdefg
 ‾‾‾
          

にはマッチせず、

abcdefgh
‾‾‾
          

にはマッチするということになります。

また、同じように行末を表す文字もあります。1行なら文字列の最後ですね。ここから先は、文字列は1行だということで解説を進めます。

それは、abc$という書き方です。この場合、

aaaabcdefg
   ‾‾‾
          

abcdefg
‾‾‾
          

にはマッチせず、

aaabc
  ‾‾‾
        

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

これらを組み合わせて、

^abc$

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

abcdef
‾‾‾
        
aaaaabc
    ‾‾‾

にはマッチせず、

abc
‾‾‾

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

最後に、 \S \Dを紹介します。\Sは「\s以外」、\Dは「\d以外」にマッチします。たまに使います。

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

正規表現は、よく使うものはだいたい解説しましたが、他にもいろいろな書き方があります。自分で調べてみましょう。

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