このページの本文へ

ググらなくても自信が持てる!JavaScriptの正規表現で最低限覚えたいこと

2016年07月05日 09時34分更新

文●Kevin Yank

  • この記事をはてなブックマークに追加
本文印刷
使いこなせると便利な正規表現。でもあの呪文のような文字を見ると腰が引けてしまう…。そんなWeb制作者のために、JavaScriptの正規表現の基礎と使い方を簡単なコードで解説。手を動かしながら試してみて。

この記事は6月2日に更新されました。更新の内容はCodePenデモの追加、コード例の更新、結論のリライト、そしてフォーマット問題の解決です。

Perlでのプログラミング経験、またはUnixシステムを管理した経験がある人にとって、JavaScriptの正規表現はとてもなじみがあるものでしょう。JavaScriptがPerl形式の正規表現をサポートしているのは本当に朗報です。

この後、そもそも正規表現とは何か、どのようなことができるのか、正規表現のもっとも普遍的な機能(すでに詳しい人は飛ばしてしまって構いません)に続いて、2つの実践的な例を交えながらJavaScriptで正規表現をどのように使うのかを説明します。読み終わる頃には、新しく身につけたテキストスキルで周りをびっくりさせられるはずです。

正規表現とは?

正規表現は特別な(最初は難しいと感じられる)コードを使い、テキスト文字列の中のパターンを検知します。サイト訪問者に自身の情報を入力させるためのHTMLフォームを表示する場合、たとえば電話番号入力のためのフィールドが1つ必要になります。注意したいのは、Webサイト訪問者の中には指示に上手に従える人とそうでない人がいるということです。

テキストフィールドの横に「電話番号を入力する欄です」というヒントを出しても(北米であれば「(XXX) XXX-XXXX」など)、間違える人はいます。番号が順番通り書かれているかどうかを確かめるために、入力されたすべての文字をチェックするスクリプトを適切な場所にカッコとダッシュを加えて書くというのは、とても大変なコーディング作業です。それでも、電話番号入力欄は比較的簡単なほうです! ユーザーがメールアドレスや、さらにややこしい場合はURLを正しく入力しているかどうかを調べなければならないとしたらどうでしょうか。

正規表現は文字列にパターンをマッチさせる方法を、迅速かつ簡単に提供します。電話番号の例なら、文字列が適切にフォーマットされた電話番号になっているか否かに関わらず、たった1つのステップで簡単な正規表現を書いてチェックに使えるのです。この例についてはあとで詳しく説明しますが、いくつかの技術的な要素の解説をします。

正規表現はどのように見えるのか

正規表現はときにとても複雑に見えますが、突き詰めるとただのテキスト文字列にすぎません。たとえば、次に挙げているのは引用符がないテキスト「JavaScriptを検索する正規表現です。

JavaScript

なんてことないでしょう? 「JavaScript」というテキストを含むどんな文字列も、この正規表現にマッチします。このように、正規表現を使うと特定のテキスト文字列を含む文字列の検索ができます。ただ困るのは、いつも簡単にできるとは限らないことです。

上に挙げたように、正規表現で使える特別なコードがあります。そのうちいくつかは実に複雑で覚えにくく、今後使っているのであれば納得のいく説明が欲しいと感じるかもしれません。ラッキーなことに、JavaScriptはPerlとまったく同じ正規表現構文をサポートしていて、Perlの正規表現をまとめたドキュメントやWebサイトはたくさんあります。その中でもこのサイトはなかなか良いものです。また、official Perl manual(Perlの公式マニュアル)にもまとまっています。両方ばらばらでは分かりにくいので、2つを同時に参照すると良いかもしれません。

それではいくつかの例と一緒に、基本的な正規表現を説明します。

基本的な構文

キャレット(^)は文字列の始めを、ドル記号($)は文字列の終わりを表します。

JavaScript   //  「Isn't JavaScript great?」 はマッチします
^JavaScript  //  「JavaScript rules!」 はマッチしますが、
             //   「What is JavaScript?」 はマッチしません
JavaScript$  //  「I love JavaScript」はマッチしますが、
             //   「JavaScript is great!」 はマッチしません
^JavaScript$ // 「JavaScript」にはマッチしますが、それ以外は何もマッチしません

正規表現構文の特別な意味を示すのではなく、^$や、ほかの正規表現構文の特殊文字を通常の文字として使いたいときがあります。文字の特別な意味を除去するには、バックスラッシュ(\)を前につけます。

\$\$\$      //  「Show me the $$$!」 にマッチします

文字列を正規表現として定義するには、角カッコを使います。たとえば次の正規表現は1から5までのすべての数字にマッチしています。

[12345]     //  「1」 と「3」はマッチしますが、  「a」または「12」はマッチしません

数字や文字の範囲も指定できます。

[1-5]       // 前の例と同じ
[a-z]       // アルファベットの小文字にマッチします
[0-9a-zA-Z] // 任意のアルファベットまたは数字にマッチします

前カッコの直後に^を入れると、文字列が変換され、記載されている以外の文字にマッチします。

[^a-zA-Z]   // アルファベット以外にマッチします

?+のような文字にも特別な意味があります。少し詳しく説明すると、?には「前の文字はオプション」、+には「前の文字が1つまたはそれ以上」、には「前の文字が0あるいはそれ以上」という意味があります。

bana?na     //  「banana」 と 「banna」 にマッチしますが、
            //  「banaana」 にはマッチしません
bana+na     //  「banana」 と 「banaana」 にマッチしますが、
            //  「banna」はマッチしません
bana*na     // 「banna」と 「banana」 と 「banaaana」にマッチしますが、
            //  「bnana」 はマッチしません
^[a-zA-Z]+$ // 1つ以上の任意のアルファベットにマッチしますが、 
            // それ以外にはマッチしません

丸カッコは文字列をまとめ、?+*を全体に適用できます。

ba(na)+na   //  「banana」 と「banananana」にマッチしますが、
            // しかし 「bana」あるいは 「banaana」にはマッチしません

丸カッコは一致する可能性のある文字列の定義に使われることもあります。パイプ(|)を使って文字列を分離します。

^(ba|na)+$  //  「banana」 と「nababa」と 「baba」はマッチしますが、
            // 「nana」「ba」「na」などはマッチしません

正規表現で文字を一致させるために使われる特別なコードは次の通りです。

.       // 改行以外の任意の文字 
\n      // 改行文字 
\r      // キャリッジリターン文字
\t      // タブ文字
\b      // 単語の区切り(単語の開始または終了)
\B      // 単語の区切り以外
\d      // 任意の数字 ( [0-9]と同じ)
\D      // 任意の数字のもの( [^0-9]と同じ)
\s      // シングル空白文字(スペース、タブ、改行など) 
\S      // シングル空白文字以外の文字 
\w      // 単語文字( [A-Za-z0-9_]と同じ)
\W      // 単語文字以外の文字( [^A-Za-z0-9_]と同じ)

これまで説明してきた特別文字とは対照的に、上に挙げた文字を特別コードとして扱うためには、バックスラッシュをエスケープシーケンスとして使います。

正規表現にはほかにも特別なコードや構文トリックがあり、すべて解説するべきですが、この記事での説明としては十分なので割愛します。

JavaScriptで正規表現を使う

JavaScriptで正規表現を使うのはとても簡単で、なぜもっと多くの人が使わないのかが不思議なほどです。JavaScriptでは正規表現を次のように作成できます。

var myRE = /regexp/;

上のコードは、regexpが正規表現になっています。たとえば、次のようにすると前の項で説明した文字列「JavaScript」を検知する正規表現を作成できます。

var myRE = /JavaScript/;

同様に、前の項の最後に紹介した正規表現は次のように作成します。

var myRE = /^(ba|na)+$/;

デフォルトではJavaScriptの正規表現では大文字と小文字が区別され、指定された文字列の最初の一致だけを検索します。文字列全体で正規表現を検索し、大文字か小文字かを無視するには、グローバル検索のg(globalのg)、大文字・小文字を無視するi(ignore caseのi)を/のあとにそれぞれ加えます。次の正規表現はその例です。各正規表現で、文字列 "test1 Test2 TEST3" のどの文字列が一致するのかの結果も記しました。

RegExp一致するもの
/Test[0-9]+/“Test2”のみ
/Test[0-9]+/i“test1”のみ
/Test[0-9]+/gi“test1”、“Test2、“TEST3”

正規表現を使うのは簡単です。テキスト文字列を含むJavaScript変数はすべて、正規表現とともに機能する3つのメソッド(もっと分かりやすく言うと機能)、match()replace()search()をサポートしています。

match()

match()は、パラメータとして正規表現を使い、検索した文字列で見つかったすべての一致する文字列の配列を返します。一致する文字列が見つからない場合、match()は「失敗」となります。冒頭の例に戻ると、ユーザーが電話番号として入力して文字列が「(XXX) XXX-XXXX」のフォームになっているかどうかをチェックには、次のような作業をします。

function checkPhoneNumber(phoneNo) {
  var phoneRE = /^\(\d\d\d\) \d\d\d-\d\d\d\d$/; 
  if (phoneNo.match(phoneRE)) {
    return true; 
  } else {
    alert( "The phone number entered is invalid!" );
    return false;
  }
}

最初にしなければならないのは、正規表現の定義です。理解を深めるためにより簡単に説明すると、正規表現は^から始めます。どのような文字列に一致させるかにかかわらず、文字列の最初からスタートしなければいけません。次に丸カッコの最初に一致する(です。正規表現構文の特別な意味を取り除くためにバックスラッシュを前に置きます。

先にも説明した通り、\dはどの数字にも一致する特別なコードです。したがって、\d\d\dはどのような3桁の数字とも一致します。同じ効果があるコードとして[0-9][0-9][0-9]も紹介しましたが、\d\d\dのほうが短いですね。

パターンの残りの部分は見るだけでも分かりやすいと思います。)は丸カッコの終わり部分に、スペースは電話番号内のスペースに、そして\d\d\d-\d\d\d\dはダッシュの前の3桁とダッシュの後の4ケタの数字に一致します。最後に、$は文字列末と一致することを示します。

ちなみに、これまでに紹介していないもう1つのショートカットを使って、この正規表現を次のように短くすることもできます。この仕組みを理解できた人には素質があります!

var phoneRE = /^\(\d{3}\) \d{3}-\d{4}$/;

そして、phoneNo.match(phoneRE)truefalseのどちらかをチェックします。言い換えれば、phoneNoに含まれる文字列が正規表現に一致するかを確認します(こうして配列が返され、JavaScriptがtrueに評価されます)。一致が検知されると、関数はtrueを返し、文字列が正しい電話番号であると確認されます。違う場合は関数はfalseを返し、警告メッセージを表示します。

この手の関数のもっとも一般的な使い方は、ユーザーがフォームに入力した内容の送信前の検証です。onSubmitイベントハンドラーで呼び出せば、情報が正しい形式で入力されていないフォームの送信を防げます。checkPhoneNumber()関数の簡単な例がこちらです。

<form action="...">
  <label>Enter phone number (e.g. (123) 456-7890): 
    <input type="text" name="phone">
  </label>
  <input type="submit">
</form>

<script>
  var form = document.querySelector('form');
  form.addEventListener('submit', function() {
    return checkPhoneNumber(this.phone.value);
  });
</script>

これで、電話番号が入力されていない場合はユーザーがフォームを送信できなくなります。もしこのまま送信しようとするとcheckPhoneNumber()によりエラーメッセージが表示されます。

replace()

名前からも分かるように、replace()は一致した箇所を新しい文字列の正規表現と置き換えます。「Cのあと以外は、IはEの前に置かれる」というミスタイプの古い格言に基づいて「acheive」、「cieling」などのミスタイプを直したいとします。ここで必要になるのが、文字列を取り出して「検索して置換する」2つの機能です。初めに「cie」を「cei」に置換します。

コードは次のようになります。

theString = theString.replace(/cie/gi,"cei");

とてもシンプルです。最初のパラメーターは検索していた正規表現であり(「大文字・小文字を無視する」、「グローバル」になっています)、次のパラメーターは置換を必要とする文字列です。

次に取り組む置換はもっと複雑です。「xei」を、「x」が「c」以外の文字である場合「xie」に置換します。「xei」を検索するための正規表現はなかなか分かりやすいです。

/[abd-z]ei/gi

この正規表現は「c」以外の文字(「a」「b」「d」、そして「z」まで)の「ei」と続くすべての文字を検出します。また、グローバル検索で、大文字と小文字を区別しない検索がかかります。

複雑になるのは、置換文字列を定義するときです。もちろん「xie」を含む一致を置換したいのですが、「x」が難しいのです。そう、「x」を一致文字列に出てくるどんな文字とも置換しなければなりません。これには新しい技を覚える必要があります。

先ほど、丸カッコが正規表現(^(ba|na)+$など)でどのように代わりの表現を定義するのかを解説しました。ここで、丸カッコにはもう1つの意味があり、挿入句は一致の一部を「覚え」て置換文字列の中で使えるのです。この場合、覚えたいのは正規表現の[abd-z]に対応する一致の要素です。だから正規表現は丸カッコに囲まれています。

/([abd-z])ei/gi

置換文字列を特定するとき、$1を丸カッコに囲まれた正規表現の要素に反応する文字列要素を挿入したい場所に置きます。よって、必要な代替文字列を操作するためのコードは次のようになります。

theString = theString.replace(/([abd-z])ei/gi,"$1ie");

要約すると、スペルミスを自動的に補うオートコレクト機能の完全版は次のようになります。

function autoCorrect(theString) { 
  theString = theString.replace(/cie/gi,"cei"); 
  theString = theString.replace(/([abd-z])ei/gi,"$1ie"); 
  return theString; 
}

この機能を実際に自分のページで使うときには、「Cのあと以外は、IはEの前に置かれる」には例外があることを知っておかなくてはなりません。ちょっと奇妙ではありますが…。

search()

search()機能は文字列の代わりに正規機能を使用するという以外は、有名なindexOf()関数に似ています。最初に一致する文字列がある正規表現で検索し、文字列内の位置を示す整数(文字列の最初に一致すれば0、10文字の文字列の最後であれば9)を返します。一致するものが見つからない場合の値は-1になります。

var theString = "test1 Test2 TEST3";
theString.search(/Test[0-9]+/); // 6

最後に

正規表現はユーザーの入力内容を検証するための貴重なツールです。JavaScriptの正規表現サポートを活用すれば、データ入力時に検証ができ、ユーザーエクスペリエンスもよりスムーズになります(:セキュリティの確保とJavaScriptが無効な場合に備えるため、サーバー側の検証はそれでも必要です)。

(原文:Regular Expressions in JavaScript

[翻訳:加藤由佳]
[編集:Livit

Web Professionalトップへ

WebProfessional 新着記事