JavaScriptの正規表現構文 第1回 正規表現の構文とフラグを理解する
JavaScriptにおける正規表現のパターンとして記述できる「構文」について、詳しく解説していきます。まずは正規表現の前提知識を確認し、フラグについて解説します。
- カテゴリー
- JavaScript/TypeScript >
- 正規表現
発行
はじめに
編集部より
本シリーズは2024年11月に開催の「JSConf JP」で杉浦が発表した「30 Minutes to Understand All of RegExp Syntax」を元に構成されています。
このシリーズでは、JavaScriptにおける正規表現、特にその正規表現のパターンとして記述できる「構文」について、詳しく解説していきます。
JavaScriptの規格であるECMAScriptのバージョンで言うと、ES2024までの構文を取り上げていきます。現在も策定中のES2025についても、できる限りキャッチアップする予定です。
正規表現それ自体を知っている人・使っている人は多いと思いますが、その構文の意味や他にどういった書き方ができるのかなど、その詳細まできちんと理解できている人は少ないのではないでしょうか。
ここを把握できていないと、AIが提案してきたのは古い構文の正規表現なのに、それに気づかずそのまま使ってしまったり、もっと効率的な書き方に気づかないということにもなりますので、この機会に知っておきましょう。
正しく覚えて、正しく使おう、正規表現
正規表現の構文の意味をきちんと理解せずにコピペで使ってばかりいると、コードに無駄な部分が増えたり、バグの原因になったりします。AIにコードを書かせたグリオ君も反省しているので、ぜひこちらもご一読を。
正規表現インスタンス
構文の話を進めていく前に、前提知識をおさらいしておきます。
JavaScriptのコードとして正規表現を使う場合、正規表現オブジェクトのインスタンスを取得する必要があり、2つの取得方法があります。
まず、/パターン/フラグの形で記述する「正規表現リテラル」です。
リテラルを使う
let re = /codegrid/v;注意点としては、パターンをスラッシュ(/)で囲んで記述するため、パターンを空にすると//になってしまい、行コメントと判別されてしまいます。また、パターンの先頭の文字を*にするのも、ブロックコメント(/*)になってしまいます。
エラーとなる正規表現リテラルの例
const re0 = //; //正規表現ではなくコメントとみなされる
const re = /*abc*/; //ブロックコメントとみなされるこのように不正な構文の場合、JavaScriptファイルが読み込まれた時点でエラーになるので、おかしなコードが実行されたりしないのが、正規表現リテラルの利点です。
もう1つの書き方として、RegExpコンストラクタを使って書くこともできます。次のように、引数には文字列リテラル("pattern"や'pattern')、またはテンプレートリテラル(`pattern`)を渡します。
コンストラクタを使う
let re = new RegExp("codegrid", "v");コンストラクタを使用する場合の特徴は、変数を利用するなど、ランタイムで動的な正規表現を生成できることです。
しかし裏を返せば、そこに構文エラーがあったとしても、ファイルを読み込んだ時点では何も起きず、実行時になってはじめて構文エラーだとわかります。
このため、可能な限り静的なリテラルの使用を推奨します。本シリーズでも、例示にはリテラル構文を使って解説していきます。
RegExpコンストラクタの注意点
RegExpコンストラクタでは引数を省略することも可能で、その場合はデフォルトで空の正規表現/(?:)/が設定されます。
const re0 = new RegExp(); // デフォルトでは空の正規表現 (/(?:)/)
const re1 = new RegExp("abc", "d"); // /abc/dコンストラクタの引数は文字列として解釈されるため、特殊な記号などはエスケープする必要があります。
たとえば、文字としてバックスラッシュ(\)を表現するためには、エスケープ用のバックスラッシュに続けて記述します。
const re2 = new RegExp('Hello, "JSConf (\\d+)"!', "u"); // /Hello, "JSConf (\d+)"!/u正規表現リテラルの場合は、こういったエスケープは不要です。
また、あまり使うことはないと思いますが、RegExpコンストラクタではUnicodeエスケープシーケンスも利用できます。
この例では、「こんにちは」という文字列をエスケープシーケンスを使って生成し、それを正規表現パターンとして利用しています。
let こんにちは = `\u3053\u3093\u306b\u3061\u306f`; // "こんにちは"
const re3 = new RegExp(こんにちは, "v"); // /こんにちは/v
const re4 = new RegExp("こんにちは", "v"); // /こんにちは/vフラグ
正規表現は、パターンとフラグの組み合わせで定義します。「パターン」は探したい文字列、「フラグ」はそのオプション設定です。
正規表現リテラルの場合は、パターンのすぐ後ろの/のあとにつけられる文字列がフラグで、RegExpコンストラクタの場合は第二引数に指定します。
リテラルのパターンとフラグの組み合わせ
let re = /pattern/dgimsvy;このdgimsvy部分がフラグで、以下のルールがあります。
- 省略可能
d、g、i、m、s、v、u、yのみ使用可能- 小文字のみ
- 並び順は自由
- 同じフラグを重複して書くとエラー
u(Unicode)とv(Unicode Sets)を同時に指定するとエラー
ES2024の時点で使えるフラグは、全部で8種類あります。
- in
dices:hasIndices:マッチした位置のインデックスを取得 global:global:全体を対象に検索insensitive:ignoreCase:大文字・小文字を区別しないmultiline:multiline:複数行にまたがる検索を可能にするsingle line:dotAll:.(ドット)が改行文字にもマッチするようにするunicode:unicode:Unicodeモードを有効にするvnicode:unicodeSets:Unicodeモードを有効にし、さらに拡張- stick
y:sticky:現在の位置からのみ検索
なお、このシリーズは、正規表現パターンの「構文」について解説する趣旨のため、そのフラグが「構文」に影響を与える場合を除いて、その挙動を解説することはしません。別途リファレンスを参照するなどしてください。
Unicodeに関する2つのフラグ
uとvフラグはUnicodeに関するフラグで、パターン内で利用できる構文に影響するため、ほかのフラグと比べて少し特殊です。
vフラグはuフラグが拡張されたもので、ES2024で追加されました。uフラグよりも使える構文が増えたり、曖昧な表現が禁止されたりしています。
詳細は追って解説しますが、ベストプラクティスとしては、できる限りvフラグを使いましょう。現状でもっとも洗練された構文が利用できるため、些細なミスを防ぐことができます。
vフラグは実はすでに使っている?
「vフラグは初めて聞いた」と思う人もいるかもしれませんが、実はHTMLのinputタグのpattern属性でも使われているものです。
<label for="pin">PIN: (4 digits)</label>
<input
id="pin"
name="pin"
type="password"
required
pattern="\d{4}"
/>この例では、input要素にpattern属性を指定し、「4桁の数字だけを受け入れる」という正規表現を書いています。vフラグに対応した動作環境であれば、自動的に利用されます。
ここまでのまとめ
正規表現オブジェクトのインスタンスは、2つの方法で取得できます。
- 正規表現リテラル:
/パターン/フラグ RegExpコンストラクタ:new RegExp(パターン文字列,フラグ文字列)
コンストラクタは動的な正規表現を生成できるが、基本的にはリテラルを使ったほうがよいです。
また、パターンと合わせて指定するフラグも、古い動作環境をサポートしなければならないといった理由がないのであれば、常にvフラグを使用して最新の構文でパターンを記述しましょう。
次回の記事からは、いよいよパターン部分に記述できる構文について解説していきます。