正規表現について、理解できていますか?
JavaScriptで文字列関連の処理を実装する際に覚えておきたいのが、正規表現と呼ばれる記法。しかし、一見しただけでは記号の意味などが理解できず、どのように応用すればよいかが分からない人もいることでしょう。
今回は、正規表現とは一体何なのかについて、徹底解説していきます。頻出パターンや正規表現を用いるメソッドなども紹介するので、ぜひ参考にしてください。
正規表現とは?
正規表現とは、ある文字列の中にある特定の文字列が含まれているかを判定するために記述される、特殊な文字列パターンです。実際に例を示して、正規表現の仕組みを説明しましょう。
例えば、アルファベットや数字などがランダムに入力された文字列の中に、アルファベットの「a」が含まれているのかを判定したいとします。このとき、正規表現を利用すると以下の文字列パターンで検索可能です。
let str_1 = "132/@-8^.;:"; // ランダムな文字列
let str_2 = "132/a-8^.;:"; // ランダムな文字列
// aが含まれているかを判定するための正規表現
let pattern = /a/;
console.log( str_1.match(pattern) ? "ヒット" : "該当なし"); // 結果:該当なし
console.log( str_2.match(pattern) ? "ヒット" : "該当なし"); // 結果:ヒット
5行目で記述されている「/a/」が、正規表現を用いた特殊な文字列です。含まれているかを判定したい文字をスラッシュで囲むのが基本的な構造です。
正規表現の文字列は、match()メソッドで引数として渡すことで文字列が存在するかを判定するために使います。今回は変数str_2に「a」が含まれているため、8行目の処理は「ヒット」と出力されることが分かります。
次に、「a」「b」「c」のどれか1文字が含まれているかを判定するとしましょう。この場合は、正規表現で以下のように記述します。
pattern = /[abc]/;
abcのどれか一文字が含まれているかを判定するためには、対象文字を大カッコで囲います。
次に、アルファベットの小文字が10文字以上、連続している部分が存在するかを判定したい場合は、以下のように表現します。
pattern = /[a-z]{10,}/;
アルファベットの小文字すべてが対象となる場合は「a-z」と記述します。また、「{10,}」は直前の文字列が10文字以上連続しているかを示します。
このように、検索したい文字列と記号を組み合わせることで、より複雑な文字列検索処理を実装できるのが、正規表現の大きなメリットです。
正規表現で頻出する主なパターン
前節「1 正規表現とは?」で取り上げたもの以外にも、JavaScriptではさまざまな正規表現パターンが存在します。特に、正規表現で使われる記号は種類が多く、各記号の違いを理解することが、正規表現を使いこなすうえでのポイントになります。
下記表に、正規表現で頻出するパターンを掲載したので、違いを理解しておきましょう。
パターン | 意味 | 例 |
---|---|---|
[^123] | 「1、2、3」以外の文字が存在するか | 「123ab56」→ヒット 「1231221」→ヒットせず |
ap* | 「a」の直後に0回以上「p」が連続で現れる文字列が存在するか | 「apple」→ヒット 「ankle」→ヒット 「uncle」→ヒットせず |
ap+ | 「a」の直後に1回以上「p」が連続で現れる文字列が存在するか | 「apple」→ヒット 「ankle」→ヒットせず 「uncle」→ヒットせず |
ap? | 「a」の直後に「p」が0または1回現れ、その後に「le」が続く文字列が存在するか | 「apple」→ヒットせず 「alert」→ヒット 「staple」→ヒット |
^th | 対象文字列が「th」から始まっているか | 「this」→ヒット 「birth」→ヒットせず |
th$ | 対象文字列が「th」で終わっているか | 「this」→ヒットせず 「birth」→ヒット |
a{3} | 「a」が3回以上連続で現れる文字列が存在するか | 「aa」→ヒットせず 「aaa」→ヒット 「aaaa」→ヒット |
a{4,6} | 「a」が4~6回連続で現れる文字列が存在するか | 「aaa」→ヒットせず 「aaaa」→ヒット 「aaaaa」→ヒット 「aaaaaa」→ヒット 「aaaaaaa」→ヒットせず |
is|at | 「is」または「at」が現れる文字列が存在するか | 「This」→ヒット 「That」→ヒット 「There」→ヒットせず |
\d | 半角数字の文字列が存在するか | 「12345」→ヒット 「aiu45」→ヒット 「aiueo」→ヒットせず |
\D | 半角数字以外の文字列が存在するか | 「12345」→ヒットせず 「aiu45」→ヒット 「aiueo」→ヒット |
\w | アルファベット・数字・アンダーバーのいずれかが現れる文字列が存在するか | 「12345」→ヒット 「aiu45」→ヒット 「aiueo」→ヒット 「@23eo」→ヒット 「@^-!_」→ヒット 「@^-!{」→ヒットせず |
\W | 「アルファベット・数字・アンダーバー」以外が現れる文字列が存在するか | 「12345」→ヒットせず 「aiu45」→ヒットせず 「aiueo」→ヒットせず 「@23eo」→ヒット 「@^-!_」→ヒット 「@^-!{」→ヒット |
Javascriptでの正規表現の作り方
JavaScriptにおける正規表現の作り方には、2種類存在します。それぞれどのように記述するのか、詳しく解説していきます。
正規表現リテラルを使う
1つ目は、正規表現リテラルを使う記述方法です。正規表現リテラルとは、「1 正規表現とは?」で紹介したサンプルコードにもあるように、スラッシュで文字列を囲む方法です。
半角数字のいずれかが存在するか判定するために正規表現を利用する場合、正規表現リテラルで以下のように記述します。
let pattern = /is|at/;
正規表現リテラルでは、最後にオプションフラグと呼ばれる文字を付け加えることで、高度な検索処理を実装できます。主なオプションフラグを以下の表に示します。
オプションフラグ | 意味 |
---|---|
g | 一致する文字列をすべて検索 |
i | アルファベットの小文字・大文字を区別せず検索 |
RegExpクラスのコンストラクターを使う
2つ目は、RegExpクラスのコンストラクターを使う方法です。RegExpは、JavaScriptのクラスの1つであり、正規表現による文字列検索処理を実装するために使われます。コンストラクターの使い方を、以下のサンプルコードで説明します。
let reg_1 = new RegExp(/is|at/); // RegExpによる正規表現生成
let reg_2 = new RegExp(/is|at/, 'g'); // 第2引数:オプションフラグ
コンストラクターには、正規表現リテラルをそのまま記述します。オプションフラグは、第2引数で文字列として指定します。
Javascriptで正規表現を扱うためのメソッド
JavaScriptで正規表現を用いるときは、メソッドと併用することがほとんどです。したがって、正規表現を扱うためのメソッドについて理解することで、正規表現を使いこなせるようになります。
ここでは、主なメソッドを5つ紹介しましょう。
match()メソッド
match()メソッドは、正規表現で文字列を検索するのに使われるメソッドです。
正規表現リテラルまたはRegExpオブジェクトを引数にとり、判定結果によって内容が変化する配列を返します。サンプルコードは以下の通りです。
let str = "This is a pen.";
let reg_lit_1 = /is/;
let reg_lit_2 = /is/g;
let reg_lit_3 = /at/;
let res_1 = str.match(reg_lit_1); // 1つでも存在したら探索終了
let res_2 = str.match(reg_lit_2); // 文字列すべてを探索
let res_3 = str.match(reg_lit_3); // 存在しないならnullを返却
if (res_1) {
console.log("index:" + res_1.index); // 存在する文字列の先頭位置
console.log("input:" + res_1.input); // 検索対象の文字列
console.log("length:" + res_1.length); // 存在する文字列の個数
} else {
console.log("存在しません");
}
if (res_2) {
console.log("length:" + res_2.length); // 存在する文字列の個数
} else {
console.log("存在しません");
}
if (res_3) {
console.log(res_3);
} else {
console.log("存在しません");
}
「This is a pen.」の文字列内に「is」または「at」が存在するか判定する処理を正規表現で実装しています。「is」は文字列内に存在することから、変数res_1・res_2には配列が返されます。一方、「at」は存在しないため変数res_3に配列は返されません。
配列の中には、文字列が最初に存在する位置を示すindex(11行目)、検索対象の文字列が格納されているinput(12行目)、ヒットした文字列の個数を示すlength(13行目)が格納されています。
オプションフラグ「g」が付与されている場合は、検索対象の文字列の最後まで探索するように動作するため、lengthの数が変化します。今回は2つ「is」が存在するため、lengthは2です。
exec()メソッド
exec()メソッドはRegExpクラスのメソッドです。探索対象の文字列を引数にとり、ヒットした文字列の開始位置などが格納された配列を返し、ヒットしなかった場合はnullが返されます。ここまでは、match()メソッドと同様の動作といえます。
match()メソッドとの大きな違いは、オプションフラグ「g」を付与していた場合の動作です。
オプションフラグ「g」を付与した状態でexec()メソッドを呼び出し、文字列がヒットしたときは、RegExpオブジェクトのlastIndexが更新されます。以下のサンプルコードで、動作を詳しく説明しましょう。
let str = "This is a pen.";
let reg_obj = new RegExp(/is/, 'g');
let count = 0;
while ((res = reg_obj.exec(str)) !== null) {
count++;
console.log("出現位置(" + count + "回目):" + res.index);
// 文字列がヒットした場合
// RegExpオブジェクトのlastIndexが更新される
// 次回exec()メソッドを呼び出した際に
// lastIndexの位置から文字列を探索するように動作する
console.log("次回探索開始位置:" + reg_obj.lastIndex);
// 結果(1回目):4
// 結果(2回目):7
}
// 文字列全体を探索し終えたらlastIndexは0に更新
console.log(reg_obj.lastIndex + "(探索終了)"); // 結果:0
lastIndexは、文字列がヒットした後も引き続き文字列がヒットするかを探索する場合に、次回探索を開始する位置が格納されます。
例えば「This is a pen」の「is」が存在するか探索する際、1回目にexec()メソッドを実行したときは、res.indexに2が格納され、lastIndexに4が格納されます。
しかし「is」が存在するのは1個だけではありません。「is」をすべて洗い出すためには、再度exec()メソッドを呼び出す必要があります。lastIndexは、2回目以降にexec()メソッドを呼び出す際に、直前にヒットした文字列よりも後ろの位置から、文字列探索を再開するために存在しているのです。
実際、2回目のexec()メソッド呼び出し時は、4文字目から探索を再開するため2つ目の「is」がヒットし、res.indexは5、lastIndexは7に更新されていることが分かります。
このようにexec()メソッドは、対象文字列内にヒットする文字列が複数存在する場合に、何度もメソッドを呼び出すことが前提となります。
一方、オプションフラグ「g」を付与せずに実行した場合、lastIndexが更新されず無限ループに陥るおそれがあるため、exec()メソッドを使う際は注意しましょう。
test()メソッド
test()メソッドは、exec()メソッドと同様にRegExpオブジェクトのメソッドで、検索対象の文字列を引数にとります。
一方、exec()メソッドのように配列を返すのではなく、文字列がヒットしたか否かの判定結果をBoolean値で返します。サンプルコードは以下の通りです。
let str = "This is a pen.";
let reg_obj_1 = new RegExp(/is/);
let reg_obj_2 = new RegExp(/at/);
// ヒットした場合はtrueが返却
console.log(reg_obj_1.test(str));
// ヒットしない場合はfalseが返却
console.log(reg_obj_2.test(str));
文字列が存在するか否かを判定する場合はtest()メソッド、文字列の位置などの詳細な情報を合わせて取得したい場合は、exec()メソッドや次にご紹介するsearch()メソッドを使うようにしましょう。
search()メソッド
search()メソッドは、match()メソッドと同じく文字列オブジェクトのメソッドで、正規表現リテラルやRegExpオブジェクトを引数にとります。
文字列がヒットしたら、最初にヒットした文字列の開始位置が返され、ヒットしなかったら-1が返されます。サンプルコードは以下の通りです。
let str = "This is a pen.";
let reg_obj_1 = new RegExp(/is/);
let reg_obj_2 = new RegExp(/at/);
// ヒットした場合は開始位置(2)が返却
console.log(str.search(reg_obj_1));
// ヒットしない場合は-1が返却
console.log(str.search(reg_obj_2));
search()メソッドは、ヒットした際に文字列の出現開始位置(match()メソッドの戻り値である配列のindex)が返されますが、オプションフラグ「g」の有無に関わらず、最初にヒットしたものしか返しません。
したがって、出現する文字列の開始位置をすべて取得したい場合は、match()メソッドやexec()メソッドを利用しましょう。
replace()メソッド
replace()メソッドは、文字列の検索と文字列の置換を兼ねたメソッドです。
引数は2つで、1つ目は正規表現リテラル・RegExpオブジェクト、2つ目は置換後の文字列です。以下のサンプルコードをご覧ください。
let str = "This pen is easy to use. Please use this pen.";
let reg_obj_1 = new RegExp(/pen/);
let reg_obj_2 = new RegExp(/pen/g);
let str_replaced_1 = str.replace(reg_obj_1, 'eraser');
console.log(str_replaced_1); // 最初にヒットしたpenのみeraserに置換
let str_replaced_2 = str.replace(reg_obj_2, 'eraser');
console.log(str_replaced_2); // ヒットしたすべてのpenをeraserに置換
オプションフラグ「g」を付与しなかった場合は、最初にヒットした文字列のみ置換され、付与した場合はヒットした文字列すべてを、第2引数で指定した文字列に置き換えます。
お仕事の途中ですが、少し一休みして、転職や独立について考えてみませんか🙌?
現役エンジニアが選ぶおすすめの転職エージェント11選【成功談・失敗談もあります】
レバテックフリーランスの評判ってどう?【現役エンジニアが徹底解説します】
MidWorks(ミッドワークス)の評判ってどう?【現役エンジニアが徹底解説します】
日々の業務に追われて自分を見失わないよう、
定期的にキャリアを振り返るようにしておきましょう🤲
最後に
さて、ここまでJavascriptでの正規表現の扱い方や関連のメソッドについて解説してきましたがいかがでしたか?
正規表現ではアルファベットや記号ばかり並んでいて、最初の方は敬遠してしまいがちですが、その使い方に慣れてしまえばものすごく便利な技術です。
実際の開発でも頻繁に登場することになりますので、今回の記事でしっかりとマスターしておいてくださいね。
このブログを通じて少しでも「傍(はた)を楽(らく)にする」ことができていれば嬉しく思います。
最後まで読んで頂きありがとうございました。