JavaScript初心者がよくつまづく正規表現。中級者以上であっても何となくぼんやりとしか使えないコーダーやフロントエンドエンジニアが多いのではないでしょうか。
2022年現在でもJavaScriptの正規表現は昔から変わらず、一度使えるようになればJAVAやPHPなどのサーバーサイド言語でも応用が効くので是非押さえておきたいところです。
正規表現とは
正規表現(せいきひょうげん、英: regular expression)とは、文字列のパターンを表現するための記号や文字の集合のことです。プログラミングやテキストエディタなどで文字列を操作する際に、特定の文字列パターンに一致する文字列を検索、置換、抽出する際によく使われます。
正規表現は、単純な文字列マッチングだけでなく、文字列の一部分を抽出するためのキャプチャ、繰り返しパターンの指定、複数のパターンの論理和・論理積の指定など、高度な文字列操作にも使用されます。
正規表現は、PerlやPython、JavaScriptなどのプログラミング言語や、grepやsedといったコマンドラインツールなどでサポートされています。
正規表現の表記方法
では、実際に正規表現の例を見てみましょう。よく例として挙げられる最も簡単な正規表現は郵便番号です。郵便番号(131-0045)を「規則的」に正規表現で表すと以下のようになります。
/^\d{3}-?\d{4}$/
// 「131-0045」でも「1050011」でも該当(マッチ)します
「131-0045」や「1050011」を「系統立てて規則的に」表現すると「/^\d{3}-?\d{4}$/
」と記述することが出来ます。暗号のようで面食らうかもしれませんが、一つ一つを分解して考えれば全く難しくありません。
まず正規表現は「/(スラッシュ)」で囲います。スラッシュの間にある「^\d{3}-?\d{4}$
」の意味を考えれば良いということですね。
郵便番号の文字のパターンを考えると「3桁の数字」「-(ハイフン)」「4桁の数字」に分けることが出来ます。正規表現はこのパターンを特殊な文字で表現しているだけです。つまり「^\d{3}
」「
」「-?
\d{4}$
」がそれぞれに対応しています。一旦まとめて、
最初の3桁の数字 | ^\d{3} |
ハイフン(必須ではない) | -? |
後ろの4桁の数字 | \d{4}$ |
という対応関係になっているのを頭に入れておいてください。ただ、この表では不十分なのでさらに分解して読み解いてみましょう。
簡単なところから見てみると「\d{3}」「\d{4}」はそれぞれ「数字3文字」「数字4文字」を表す正規表現です。「\d{文字数〇}」という表記は「文字数〇個の数字」ということになります。
では、「^(ハット)」と「$(ドルマーク)」は何を表しているのでしょうか。
これもすごく簡単で「^(ハット)」は文字列の「先頭」を表し、「$(ドルマーク)」は文字列の「最後・末尾」を表しています。「^\d{3}
」は「先頭の数字3文字」、「\d{4}$
」は「末尾の数字4文字」という意味になりますね。
もう一個残ってます。「-?(ハイフン、クエスチョン)」はどういう意味でしょうか。
「-(ハイフン)」は郵便番号の「-」を表していることは明白ですが、「?(クエスチョン)」が良く分からないですね。この「?」は「直前の文字が0回か1回存在する」ことを意味します。つまり、「-」が無い(0回)場合と「-」が1個あれば、正規表現としてマッチするということです。
上記をまとめて「/^\d{3}-?\d{4}$/
」を日本語で表現すると「先頭に数字が3文字あって、次にハイフンがあってもなくても良くて、末尾に数字4文字の文字列」ということになります。毎回この和訳もどきをしていると混乱するので、分かりやすく「/^\d{3}-?\d{4}$/
」という正規表現を使っているのです。
正規表現はプログラミングを用いて機械と対話する上で、文字列のパターンを効率的に伝える役割をしているということですね。
adsense
正規表現のパターン一覧
基本を押さえると正規表現内で使われている文字の意味を理解するだけで、様々なパターンを表現することが出来るようになります。先述の郵便番号は最も単純な例ですが、実際の業務ではものすごく長いURLのパラメータの検出だったり、顧客番号の判定などに利用されることの方が多いです。
そこで正規表現として使われる文字の意味を一覧でまとめておきますので、必要に応じてこの表を見返してみてください。
文字 | 意味 | 補足 |
---|---|---|
^ | 行の先頭にマッチ | – |
$ | 行の末尾にマッチ | – |
? | 直前の文字0回か1回の存在にマッチ | – |
* | 直前の文字の0回以上の繰り返しにマッチ | – |
+ | 直前の文字の1文字以上の繰り返しにマッチ | – |
\d | 数字。[0-9]と同義 | – |
\D | 数字以外。[^0-9]と同義 | – |
\w | 大文字小文字の英字、数字、_(アンダースコア)。 | – |
\W | 文字以外。[^\w]と同義 | – |
\s | 空白文字 | – |
\S | 空白以外の文字。[^\s]と同義 | – |
\t | タブ文字 | – |
\n | 改行 | – |
ABC | 文字列「ABC」 | – |
[ABC] | A,B,Cのいずれか1文字にマッチ | – |
[^ABC] | A,B,C以外のいずれか1文字にマッチ | – |
[A-Z] | AからZの間の1文字 | – |
{n} | 直前の文字をn回マッチ | – |
{n,} | 直前の文字をn回以上マッチ | – |
{m,n} | 直前の文字をmからn回マッチ | – |
\ | 文字をエスケープ | 「\’」「\.」「\*」など |
JavaScriptにおける正規表現の宣言方法
JavaScriptで正規表現を宣言して変数に代入する方法は2つあります。
let regStr = /正規表現/オプション;
let regStr = new RegExp('正規表現','オプション');
1つ目は単純に正規表現をオブジェクトに代入しているだけですね。2つ目はRegExp()
という関数で生成しています。RegExpは最初に書いた「regular expression」の略です。英語を知っていると覚えやすいですね。
2つの生成方法に共通して「オプション」があります。このオプションは正規表現をどのように扱うかを指定出来るものです。具体的には次のようなオプションがあります。
オプション | 意味 |
---|---|
g | グローバルサーチ(Globalのg)。文字列全体に対してマッチングするかどうかを判定させます。指定しない場合は1度マッチングした時点で処理が終了します。 |
i | 大文字小文字を区別しないように指定します |
m | 複数行の文字列を複数行として扱います |
u | Unicodeに対応させます。漢字の判定が必要な場合に有効です。 |
正規表現を扱うJavaScriptの関数
実際に正規表現をどのように利用するのかイメージがつかみにくいと思いますので、JavaScriptで正規表現を扱う関数を紹介します。分かりやすく一覧にすると、
test()
test()メソッドは、指定した文字列と正規表現とで一致しているかどうかを調べるための関数です。 返り値はtrueかfalseのみになります。
const str = 'Omoide ha Yabunonaka'; // 検索対象文字列
const regex = new RegExp('abu*'); // 正規表現の検索パターン
console.log(regex.test(str)); // 文字列に正規表現パターンが含まれるのでtrueが返ります
// 基本形
RegExpオブジェクト.test(検索文字列);
match()
match()メソッドは、正規表現のパターンにマッチした文字列を配列で返します。マッチしない場合はnullが返ります。gオプションを含んでる場合は、マッチする全てを含む配列を返します。
const paragraph = '8192 is GOD number. I love this number.';
const regex = /[A-Z]/g;
const found = paragraph.match(regex);
console.log(found);
// Array ["G", "O", "D", "I"] がコンソールに出力されます
// 基本形
検索文字列.match(正規表現パターン);
test()とは違って正規表現を引数に入れることに注意しましょう。微妙に文法内の順序が違うんですよね……
exec()
exec()メソッドは、指定された文字列内で一致する正規表現パターンの検索します。返り値は、検索結果の配列、該当がなければnullを返します。
const result = /(.+)cde(f)/.exec("abcdefg");
console.log(result);
// コンソールには下記配列が出力
(3) ["abcdef", "ab", "f", index: 0, input: "abcdefg", groups: undefined]
出力の結果が若干ごちゃごちゃで説明すると長くなるので別途記事でまとめます。1つだけ正規表現の中に(.+)や(f)が入っているのが分かりますか?これはある種の区切り(適切な表現か微妙ですが)を表します。コンソール結果に”ab”、”f”とあるのは、正規表現内の「(.+)」「(f)」とそれぞれ対応していることだけ頭の片隅に置いておいてください。
replace()
replace() メソッドは、正規表現パターンに一致する文字列を置き換えた置換後の文字列を返します。 パターンには文字列でも正規表現でも指定することが可能です。
パターンが文字列の場合では、最初に一致した箇所のみを置換します。
注)元の文字列は変更されません。
const p = 'The dog walks on the bridge.';
// 単なる文字列の置換
console.log(p.replace('dog', 'cat'));
// コンソール出力は「The cat walks on the bridge.」
const regex = /Dog/i;
console.log(p.replace(regex, 'hedgehog'));
// コンソール出力は「The cathedgehog walks on the bridge.」
実際には先ほどの(正規表現)
括弧と$1
,$2
などを使って置換する場合が多いです。「$」を使った置換方法は下記の記事に書きましたので併せてご参考になさってくださいね。
まとめ
- 正規表現は思っているほど難しいものではない。
- 正規表現内で使われている特殊文字の意味をひとつづつ追うことからはじめる
- 正規表現を使うシーンは紹介したJavaScriptの各メソッドを確認しておくと焦らずに済む
- 正規表現は主に文字列の検索と置換に用いられる
少し長くなりましたがJavaScriptの正規表現についてまとめてみました。初学の方も中堅の方もこれを機に正規表現の理解を見返してみてはいかがでしょうか?そんなに難しいことはありません。
【参考文献】https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/String/match
【参考文献】https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/RegExp/exec