【JavaScript】ゼロからわかる正規表現|SEラボ

JavaScript_image トップへ

はじめに

Web開発やデータ処理の現場で頻繁に登場する「正規表現」。
しかし、その独特な記述ルールに戸惑い、「難しい」「使いこなせない」と感じる方も少なくありません。

本記事では、JavaScriptの正規表現を初めて学ぶ方でも理解できるよう、順を追って分かりやすく解説します。
正規表現を使用したコードも記載しておりますので、自身のPCにコピペするだけで動きを確認することができます。

正規表現を学ぶことで、文字列の検索や置換をより効率的に行え、コーディングの幅が広がります。
「今さら聞けない…」と思っている方も、安心してください。
本記事を通じて、正規表現の基本をしっかりと身につけ、自信を持って活用できるようになりましょう!

正規表現オブジェクトの作り方

正規表現を使うことで、文字列の検索や置換を簡単に実行できるようになります。
JavaScriptで正規表現を使うためには、まず「正規表現オブジェクト」を作成する必要があります。
正規表現オブジェクトの作成方法は、下記の2つです。

スラッシュで囲む

最も簡単に正規表現を作成する方法は、スラッシュ(/)で囲む方法です。
このスラッシュで囲む方法を、専門用語で「正規表現リテラル」と呼びます。
正規表現リテラルはコードの可読性が高く、一般的に使用されています。

< 例 > スラッシュで正規表現を作成

// 正規表現を作成
const regexp = /apple/;

// 文字列を定義
const strApple = 'This is an apple.';
const strBanana = 'This is a banana.';

// 文字列に「apple」が存在するかを確認
const resultApple = regexp.test(strApple);
const resultBanana = regexp.test(strBanana);

// 結果を出力
console.log(resultApple);   // true
console.log(resultBanana);  // false

RegExpコンストラクタ関数

もう一つの方法は、RegExpコンストラクタ関数を利用する方法です。

RegExp?なにそれ?
という方のために説明しますと、正規表現という単語は「Regular Expression」を和訳したものです。
これだと長すぎるため、頭文字を取って「RegExp」というデータ型(オブジェクト型)が作られました。

このRegExp型のオブジェクトを作成するために用意されている関数が、コンストラクタ関数です。
コンストラクタ関数を利用した正規表現の作成方法は下記です。

< 例 > コンストラクタ関数で正規表現を作成

// 正規表現を作成
const regexp = new RegExp('apple');

// 文字列を定義
const strApple = 'This is an apple.';
const strBanana = 'This is a banana.';

// 文字列に「apple」が存在するかを確認
const resultApple = regexp.test(strApple);
const resultBanana = regexp.test(strBanana);

// 結果を出力
console.log(resultApple);   // true
console.log(resultBanana);  // false

どっちを使えばいいの?

基本的に、スラッシュで囲む正規表現リテラルを使用してください。

スラッシュで囲む方が可読性が高いためです。
どちらを使えばよいか、より詳しく説明すると下記の通りです。

・どんな正規表現のパターンを使用したいか決まっている場合
  → スラッシュで囲む

・パターンが変化する場合
  → < 例1 > ユーザが画面で入力した正規表現文字列を使用する想定のため、
         パターンが決まっていない。
    < 例2 > 正規表現オブジェクトを作成するコードより上の行で、
         条件分岐などをして正規表現文字列を作成するため、パターンが変化する。
         (正規表現作成コードの直前まで、どんな正規表現文字列が入るかが不明)
    → どちらの例の場合もパターンが変化するため、RegExpコンストラクタ関数

オプションフラグ

JavaScriptの正規表現には、マッチングする仕様を追加・変更できるオプションフラグがあります。
オプションフラグを活用すると、より柔軟な検索が可能になります。
ここでは、フラグの付け方と代表的な3つを解説します。

フラグの付け方

上記の「正規表現オブジェクトの作り方」で、2通りの作成方法があることを理解できたかと思います。
それぞれの場合のフラグの付け方を紹介します。

正規表現リテラルスラッシュで囲む
  → 最後のスラッシュの後にフラグを付ける
    → /パターン/フラグ;

・RegExpコンストラクタ関数
  → 第二引数にフラグを付ける
    → new RegExp(‘パターン’, ‘フラグ’);

< 例 > フラグを付ける

// 正規表現リテラルの場合
const regexp = /apple/g;

// RegExpコンストラクタ関数の場合
const regexp = new RegExp('apple', 'g');

g フラグ

グローバルフラグ(Global Flag)を表します。

通常(gフラグ無し)は、最初に一致したものだけがマッチしたと判断されます。
gフラグを付けることで、対象の文字列にある全ての一致したものが、マッチしたと判断されます。

言葉ではイメージしづらいと思うので、下記の例をご覧ください。

< 例 > gフラグを使用する

// 正規表現を作成
// 下記のどちらでも好きな形式で作成してください
// 以降は主に「正規表現リテラル」形式を使用して説明します
var regexp = /foo/;
var regexpGFlag = /foo/g;
var regexp = new RegExp('foo');
var regexpGFlag = new RegExp('foo', 'g');

// 文字列を定義
const str = 'fooboofooYYYfooxxx';  // foo が3つ含まれる文字列

// fooを空文字に置換する(= fooを削除)
const result = str.replace(regexp, '');            // gフラグ無し
const resultGFlag = str.replace(regexpGFlag, '');  // gフラグあり

// 結果を出力
console.log(result);       // boofooYYYfooxxx ← 最初の foo だけ置換
console.log(resultGFlag);  // booYYYxxx       ← 全ての foo を置換

同じ変数名を宣言してもエラーにならないように「var」で変数宣言をしましたが、仕事で使用する場合は「let」または「const」を使用してください。
( 参考サイト:【JavaScript】 let とは? const と var との違いも解説|SEラボ

i フラグ

Ignore Case フラグ(Ignore Case Flag)を表します。
このフラグは良い和訳がないため、英字のまま表現しています。

iフラグを付けることで、マッチング判定の際に大文字・小文字の区別を無視するようになります。

< 例 > iフラグを使用する

// 正規表現を作成
const regexp = /apple/;
const regexpIFlag = /apple/i;

// 文字列を定義
const str = 'This is an ApPLe.';

// 文字列に「apple」が存在するかを確認
const result = regexp .test(str);            // iフラグ無し
const resultIFlag = regexpIFlag .test(str);  // iフラグあり

// 結果を出力
console.log(result);       // false
console.log(resultIFlag);  // true

m フラグ

マルチラインフラグ(Multiline Flag)を表します。

通常(mフラグ無し)は、対象文字列が複数行で記述されていたとしても、1つの文字列として扱われます。
そのため「^」や「$」は、対象文字列全体の文頭と文末を指します。

しかしながら mフラグを付けると、対象文字列に改行が含まれていた場合は「^」や「$」が各行の文頭と文末を指すようになります。

つまり、下記の例のようにmフラグ無しの場合は全体の先頭になるため false になり、mフラグの場合は各行の先頭になるため 3行目の This が一致して true になります。

< 例 > mフラグを使用する

// 正規表現を作成
//(先頭が「This」で始まる)
const regexp = /^This/;
const regexpMFlag = /^This/m;

// 文字列を定義
//(改行を「\n」で表現しても同じ)
const str = `Is this an apple?
No!
This is a Banana.`;

// 文字列の先頭に「This」が存在するかを確認
const result = regexp.test(str);            // mフラグ無し
const resultMFlag = regexpMFlag.test(str);  // mフラグあり

// 結果を出力
console.log(result);       // false
console.log(resultMFlag);  // true

複数のオプションフラグを使う

オプションフラグを複数併用する方法は簡単で、今まで1つだけ記載していたフラグの場所に複数記述するだけで済みます。

下記の4パターンの正規表現オブジェクトを生成し、違いをコードで確認します。
 1:オプションフラグ無し
 2:iフラグ
 3:mフラグ
 4:iフラグとmフラグの併用

対象文字列を改行を含む文章、且つ途中で先頭小文字の「this」があるとします。
この場合、4つ目の併用パターンのみが true になります。

< 例 > 複数のオプションフラグを使用する

// 正規表現を作成
const regexp = /^This/;         // パターン1
const regexpIFlag = /^This/i;   // パターン2
const regexpMFlag = /^This/m;   // パターン3

// 先頭が「This」で始まる、大文字と小文字を区別しない、改行を考慮
const regexpFlags = /^This/im;  // パターン4

// 文字列を定義
//(改行を「\n」で表現しても同じ)
const str = `
Is
this
an
apple?
`;

// パターンに一致する文字列が存在するかを確認
const result = regexp.test(str);
const resultIFlag = regexpIFlag.test(str);
const resultMFlag = regexpMFlag.test(str);
const resultFlags = regexpFlags.test(str);

// 結果を出力
console.log(result);       // false
console.log(resultIFlag);  // false
console.log(resultMFlag);  // false
console.log(resultFlags);  // true

文字クラス

特定の文字や数字を表す特殊表現のことを文字クラスといいます。

例えば 0 から 9 の一文字を表す場合、「 [0-9] 」 と書くことができます。
これをさらに簡略化すると 「/d」 になります。
このように、特定の値を端的に表現したい時に文字クラスを使用します。

代表的な文字クラスを、一覧で記載します。

表現方法 説明
[xyz] 文字クラス といいます。
つまり、広義にはこの表の全てを文字クラスといい、
狭義にはこの表現方法のみを文字クラスといいます。
囲んだ文字のいずれかを表します。
. ワイルドカード といいます。
改行文字 (\n、\r、\u2028、\u2029) を除くあらゆる1文字を表します。
\d 数字文字クラスエスケープ といいます。
あらゆるアラビア数字を表します。
[0-9] に相当します。
\w 英数文字クラスエスケープ といいます。
アンダースコアを含むあらゆる半角英数字(基本ラテンアルファベット)を表します。
[A-Za-z0-9_] に相当します。
\s ホワイトスペース文字クラスエスケープ といいます。
ホワイトスペース文字を表します。
[\f\n\r\t\v\u0020\u00a0\u1680\u2000-\u200a\u2028\u2029\u202f\u205f\u3000\ufeff] に相当します。
\n 行送り文字を表します。
\f 改ページを表します。
\ 次の値を特殊文字として扱います。
特殊文字の前に付けた場合は、次の値を通常文字として扱います。
(エスケープ)
x|y 論理和 といいます。
“x” または “y” を表します。
※ 論理和は「選択肢の集合」を表す方法の一つですが、正確には文字クラスではありません。

数量子

数量子は、直前の文字や数字の個数を表します。

数量子の一覧を下記に記載します。

表現方法 説明
x? 直前のアイテム “x” の 0 回か 1 回の出現を表します。
x* 直前のアイテム “x” の 0 回以上の繰り返しを表します。
x+ 直前のアイテム “x” の 1 回以上の繰り返しを表します。
使い分けとしては、x が無いことも許容する場合は「x*」、
必ず 1 回は出現しなければならない場合は「x+」とします。
x{n} 直前のアイテム “x” がちょうど “n” 回出現することを表します。
x{n,} 直前のアイテム “x” が “n” 回以上出現することを表します。
x{n,m} 直前のアイテム “x” が “n” 回以上、”m” 回以下出現することを表します。

数量子には貪欲/非貪欲という概念が存在します。
できる限り多くの文字列と一致しようとすることを「貪欲」といい、最小の一致で検索を停止することを「非貪欲」といいます。

数量子は、デフォルトが「貪欲」で設定されています。
そのため「非貪欲」にしたい場合は、数量子の後に「?」を付けてください。

イメージしやすいように具体的に説明すると、下記の通りです。

対象文字列:<div>Win-Winブログの<span>SEラボ</span></div>

通常の正規表現:/<.*>/
  → ”<div>Win-Winブログの<span>SEラボ</span></div>” に一致

非貪欲の正規表現:/<.*?>/
  → ”<div>” に一致

アサーション

アサーションは、対象文字列中の正規表現を使う場所を指定します。

代表的な3つを紹介します。

表現方法 説明
^ 入力開始境界アサーション といいます。
対象文字列の先頭を表します。
mフラグを使用した場合は、改行の直後も先頭として扱われます。
$ 入力末尾境界アサーション といいます。
対象文字列の末尾を表します。
mフラグを使用した場合は、改行の直前も末尾として扱われます。
\b 単語境界アサーション といいます。
単語の境界を表します。
これは、下記を表します。
・単語構成文字と後に続く非単語構成文字の間
・非単語構成文字と後に続く単語構成文字の間
・文字列の先頭/末尾

正規表現の使用例

問題を解説しながら、正規表現の使用例を紹介していきます。
それではさっそく、問題を見ていきましょう!

問題1

 あなたは就職活動をしています。
 合同企業説明会に出店する企業一覧を取得するシステムを作成しました。
 取得した企業一覧の文字列「companies」に、下記のどちらかが含まれるかを判定してください。
  ・アイ・ビー・エム
  ・IBM

 この場合、下記のように考えます。
  ・どちらか → 「|」を使う
  ・判定する → true または false → 正規表現オブジェクトの test メソッド

// 正規表現を作成
const regexp = /アイ・ビー・エム|IBM/;

// 文字列を定義
const companies01 = `
三菱重工
LINEヤフーコミュニケーションズ
東京海上日動火災保険
日本IBM
日本政策投資銀行(DBJ)
`;

const companies02 = `
NTT DATA
アイ・ビー・エム
SCSK
`;

const companies03 = `
Google
Apple
Microsoft
Amazon
`;

// パターンに一致する文字列が存在するかを確認
const result01 = regexp.test(companies01);
const result02 = regexp.test(companies02);
const result03 = regexp.test(companies03);

// 結果を出力
console.log(result01);  // true
console.log(result02);  // true
console.log(result03);  // false

問題2

 あなたは郵便局で勤務しています。
 メールの本文から、郵便番号を取得するプログラムを作成してください。
 この時、メール本文に複数の郵便番号が含まれる可能性があることに注意してください。

 この場合、下記のように考えます。
  ・郵便番号 → 数字3つ、ハイフン、数字4つ → \d{3}-\d{4}
  ・複数の郵便番号 → gフラグ
  ・取得する → 文字列の match メソッド

// 正規表現を作成
const regexp = /\d{3}-\d{4}/g;

// 文字列を定義
const mailBody = `
Win-Winブログのポスターを
送付してください。
送付先は下記です。

東京都新宿区 160-0023
大阪府大阪市 530-0001
さらに別の住所 150-0012

ご不明な点がありましたら、
ご連絡ください。
`;

// パターンに一致する文字列を取得
// (一致しない場合は null )
const matches = mailBody.match(regexp);

// 結果を出力
if(matches) {
    matches.forEach((postalCode, index) => {
        console.log(`郵便番号${index + 1}:${postalCode}`)
    })
} else {
    console.log('メール本文に郵便番号が存在しません')
}

// 想定する出力結果は下記です。
// 郵便番号1:160-0023
// 郵便番号2:530-0001
// 郵便番号3:150-0012

さいごに

いかがだったでしょうか。

ここまで読んでくれたあなたは、正規表現の基礎となるオブジェクトの作成方法からオプションフラグ、文字クラス、アサーション、具体例と実践的な部分まで理解できたかと思います。

正規表現の使用頻度はそこまで高くはないですが、必ず使う機会がある技術です。
それでいてエンジニアによって理解度が異なるため、腕の見せ所でもあります。
もし、まだ理解があいまいな場合は、何度も見返すことで記憶を定着させていきましょう!

この記事で紹介した基本概念とテクニックをコードに落とし込み、ご自身の学習やプロジェクトにぜひ活用してみてください。

あなたのプログラミングスキル向上に、少しでも役に立てたなら幸いです。
今後とも、プログラミング技術について分かりやすく解説していきますので、どうぞよろしくお願いします!

コメント